Compare commits

..
14 Commits
52 changed files with 1467 additions and 777 deletions

No files matched your search

@@ -59,7 +59,10 @@ public class CompatUniformManager {
static final int MAT_FIELDS = 5;
static final int LOC_MAT_BASE = LOC_LIGHT_BASE + 2 * LIGHT_FIELDS; // 43
static final int LOC_COUNT = LOC_MAT_BASE + MAT_FIELDS; // 48
static final int LOC_IRIS_MODELVIEW_INVERSE = LOC_MAT_BASE + MAT_FIELDS; // 48
static final int LOC_IRIS_PROJECTION_INVERSE = LOC_IRIS_MODELVIEW_INVERSE + 1; // 49
static final int LOC_COUNT = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
private static final String[] LIGHT_FIELD_NAMES = {
"ambient", "diffuse", "specular", "position", "halfVector",
@@ -89,6 +92,8 @@ public class CompatUniformManager {
UNIFORM_NAMES[LOC_IRIS_NORMAL] = "iris_NormalMatrix";
UNIFORM_NAMES[LOC_IRIS_LIGHTMAP_TEXTURE_MATRIX] = "iris_LightmapTextureMatrix";
UNIFORM_NAMES[LOC_IRIS_TEXTURE_MATRIX] = "iris_TextureMatrix";
UNIFORM_NAMES[LOC_IRIS_MODELVIEW_INVERSE] = "iris_ModelViewMatrixInverse";
UNIFORM_NAMES[LOC_IRIS_PROJECTION_INVERSE] = "iris_ProjectionMatrixInverse";
UNIFORM_NAMES[LOC_ALPHA_TEST_REF] = "angelica_currentAlphaTest";
UNIFORM_NAMES[LOC_SCENE_COLOR] = "angelica_SceneColor";
UNIFORM_NAMES[LOC_CLIP_PLANES] = "angelica_ClipPlane[0]";
@@ -229,10 +234,15 @@ public class CompatUniformManager {
}
// ModelView Inverse
if (locs[LOC_MODELVIEW_INVERSE] != -1) {
if (locs[LOC_MODELVIEW_INVERSE] != -1 || locs[LOC_IRIS_MODELVIEW_INVERSE] != -1) {
mv.invert(scratchMatrix);
scratchMatrix.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_INVERSE], false, mat4Buf);
if (locs[LOC_MODELVIEW_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_INVERSE], false, mat4Buf);
}
if (locs[LOC_IRIS_MODELVIEW_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_MODELVIEW_INVERSE], false, mat4Buf);
}
}
}
@@ -266,10 +276,15 @@ public class CompatUniformManager {
}
// Projection Inverse
if (locs[LOC_PROJECTION_INVERSE] != -1) {
if (locs[LOC_PROJECTION_INVERSE] != -1 || locs[LOC_IRIS_PROJECTION_INVERSE] != -1) {
proj.invert(scratchMatrix);
scratchMatrix.get(mat4Buf);
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION_INVERSE], false, mat4Buf);
if (locs[LOC_PROJECTION_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_PROJECTION_INVERSE], false, mat4Buf);
}
if (locs[LOC_IRIS_PROJECTION_INVERSE] != -1) {
RENDER_BACKEND.uniformMatrix4(locs[LOC_IRIS_PROJECTION_INVERSE], false, mat4Buf);
}
}
}
@@ -111,6 +111,7 @@ public class DisplayListManager {
int listMode,
CommandRecorder recorder,
List<AccumulatedDraw> draws,
DisplayListCallback callback,
StackTraceElement[] stackTrace,
// Debug logging fields (only used when LOG_DISPLAY_LIST_COMPILATION)
@@ -131,6 +132,19 @@ public class DisplayListManager {
return currentRecorder != null && Thread.currentThread() == recordingThread;
}
public static UnsupportedOperationException unsupportedInList(String what) {
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
}
public static String describeCurrentCompilation() {
if (currentRecorder == null) {
return "(not compiling)";
}
final String mode = glListMode == GL11.GL_COMPILE_AND_EXECUTE ? "GL_COMPILE_AND_EXECUTE" : "GL_COMPILE";
final int depth = compilationStack.size() + 1;
return "while compiling display list " + glListId + " (" + mode + (depth > 1 ? ", nesting depth " + depth : "") + ")";
}
public static boolean isCompileAndExecute() {
return glListMode == GL11.GL_COMPILE_AND_EXECUTE;
}
@@ -159,7 +173,9 @@ public class DisplayListManager {
* Only use this if you are 100% that the transforms will not be of any use after the display list draw call.
*/
public static void discardMatrixTransforms() {
resetRelativeTransform();
if (isRecording()) {
resetRelativeTransform();
}
}
/**
@@ -748,7 +764,7 @@ public class DisplayListManager {
// Nested display list compilation violates OpenGL spec, but some of our optimizations require it
// Save current compilation context and start fresh for nested list
final CompilationContext parentContext = new CompilationContext(
glListId, glListMode, currentRecorder, accumulatedDraws,
glListId, glListMode, currentRecorder, accumulatedDraws, transformCallback,
compilationStackTrace, pendingTransformOps, multMatrixSources, drawRangeSources
);
compilationStack.push(parentContext);
@@ -784,9 +800,21 @@ public class DisplayListManager {
if (glListMode == 0) {
throw new RuntimeException("glEndList called outside of a display list!");
}
try {
finishCurrentList();
} finally {
// Non-null here means finishCurrentList threw before transferring or releasing
// ownership of the recorder.
if (currentRecorder != null) {
currentRecorder.delete();
}
// Must always run, even when compilation fails - a leaked context leaves
// isRecording() true forever and every guarded GL call throws afterward.
popCompilationContext();
}
}
final boolean isNested = !compilationStack.isEmpty();
private static void finishCurrentList() {
// Stop compiling mode (works for both root and nested lists now)
TessellatorManager.stopCapturingDirect();
@@ -803,8 +831,9 @@ public class DisplayListManager {
final boolean hasCommands = !commandBuffer.isEmpty();
if (hasCommands) {
final DisplayListVBO compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
DisplayListVBO compiledBuffers = null;
try {
compiledBuffers = new DisplayListVBOBuilder().addDraws(accumulatedDraws).build();
final IndexedDrawBatchBuilder indexedBuilder = commandBuffer.getIndexedDraws();
final List<IndexedDrawBatch> indexedBatches;
if (indexedBuilder.isEmpty()) {
@@ -824,16 +853,23 @@ public class DisplayListManager {
}
}
compiled = new CompiledDisplayList(commandBuffer.finish(), commandBuffer.getComplexObjects(), compiledBuffers, indexedBatches);
final DisplayListCommand[] complexObjects = commandBuffer.getComplexObjects();
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches);
currentRecorder = null;
} catch (Exception e) {
GLStateManager.LOGGER.error("Encountered a fatal issue while trying to compile a display list.");
e.printStackTrace();
commandBuffer.delete();
currentRecorder = null;
if (compiledBuffers != null) {
compiledBuffers.delete();
}
return;
}
} else {
// Free the recorder even if empty
commandBuffer.delete();
currentRecorder = null;
// Empty display list - per OpenGL spec, still valid after glNewList/glEndList
compiled = CompiledDisplayList.EMPTY;
}
@@ -846,6 +882,10 @@ public class DisplayListManager {
if (LOG_DISPLAY_LIST_COMPILATION) {
logCompiledDisplayList(glListId, compiled, compilationStackTrace);
}
}
private static void popCompilationContext() {
final boolean isNested = !compilationStack.isEmpty();
// Handle nested compilation - restore parent context
if (isNested) {
@@ -856,6 +896,7 @@ public class DisplayListManager {
glListMode = parentContext.listMode;
currentRecorder = parentContext.recorder;
accumulatedDraws = parentContext.draws;
transformCallback = parentContext.callback;
compilationStackTrace = parentContext.stackTrace;
pendingTransformOps = parentContext.pendingOps;
multMatrixSources = parentContext.matrixSources;
@@ -869,6 +910,7 @@ public class DisplayListManager {
recordingThread = null;
accumulatedDraws = null;
pendingDraw = null;
transformCallback = null;
compilationStackTrace = null;
pendingTransformOps = null;
multMatrixSources = null;
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import org.joml.Matrix4f;
import org.joml.Vector4f;
@@ -9,13 +9,13 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Software emulation of GL feedback mode
*/
@@ -127,12 +127,12 @@ public class FeedbackManager {
}
private static int positionStride() {
final int s = VertexAttribState.get(0).effectiveStride();
final int s = VAOManager.getAttribEffectiveStride(0);
return (s != 0) ? s : 12;
}
private static long positionOffset() {
return VertexAttribState.get(0).offset;
return VAOManager.getAttribOffset(0);
}
public static void processDrawArrays(int mode, int first, int count) {
@@ -151,7 +151,7 @@ public class FeedbackManager {
computeMVP();
final int stride = positionStride();
final int elementSize = VertexAttribState.Attrib.glTypeSizeBytes(type);
final int elementSize = VAOManager.Attrib.glTypeSizeBytes(type);
final int indexByteCount = indexCount * elementSize;
ensureIndexReadbackCapacity(indexByteCount);
indexReadbackBuf.clear().limit(indexByteCount);
@@ -255,7 +255,7 @@ public class FeedbackManager {
}
private static void readPositionBuffer(long offset, ByteBuffer destination) {
final int positionVbo = VertexAttribState.get(0).vboId;
final int positionVbo = VAOManager.getAttribVBO(0);
final int previousVbo = GLStateManager.getBoundVBO();
if (positionVbo != 0 && positionVbo != previousVbo) {
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizon.gtnhlib.bytebuf.Pointer;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
@@ -8,6 +9,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
@@ -39,13 +41,11 @@ import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import com.gtnewhorizons.angelica.glsm.states.TextureBinding;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntStack;
@@ -56,13 +56,13 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Matrix4fStack;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.Display;
@@ -97,10 +97,10 @@ import java.util.Set;
import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import static com.gtnewhorizons.angelica.glsm.Vendor.INTEL;
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
import static com.gtnewhorizons.angelica.glsm.Vendor.NVIDIA;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* OpenGL State Manager - Provides cached state tracking and management for Backend Renderer Actions
@@ -189,25 +189,25 @@ public class GLStateManager {
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
// Skip the backend vertexAttrib call in either case.
public static void flushDeferredVertexAttribs(int vertexFlags) {
if (dirtyColorAttrib && (vertexFlags & VertexFlags.COLOR_BIT) == 0) {
public static void flushDeferredVertexAttribs(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
if (!hasColor && dirtyColorAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
dirtyColorAttrib = false;
}
if (dirtyLightmapAttrib && (vertexFlags & VertexFlags.BRIGHTNESS_BIT) == 0) {
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
dirtyLightmapAttrib = false;
}
if (dirtyNormalAttrib && (vertexFlags & VertexFlags.NORMAL_BIT) == 0) {
if (!hasNormal && dirtyNormalAttrib) {
final var n = ShaderManager.getCurrentNormal();
RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z);
dirtyNormalAttrib = false;
}
if (dirtyTexCoordAttrib && (vertexFlags & VertexFlags.TEXTURE_BIT) == 0) {
if (!hasTexCoord && dirtyTexCoordAttrib) {
final var tc = ShaderManager.getCurrentTexCoord();
RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w);
dirtyTexCoordAttrib = false;
}
if (!hasLightmap && dirtyLightmapAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
dirtyLightmapAttrib = false;
}
}
// Highest texture unit index that has ever had a non-zero binding; limits onDeleteTexture scan range
@@ -456,18 +456,16 @@ public class GLStateManager {
@Getter protected static int listBase = 0;
@Getter protected static int boundVBO;
@Getter protected static int boundEBO;
@Getter protected static int boundVAO;
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0;
private static final int[] clientArraysVBOOffsets = new int[VertexAttribState.MAX_ATTRIBS];
public static int getBoundEBO() {
return VAOManager.boundEBO;
}
private static int boundPixelUnpackBuffer;
private static int boundPixelPackBuffer;
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
private static PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
static {
vaoEboMap.defaultReturnValue(0);
}
@Getter private static int defaultVAO; // Non-zero on core profile
public static void reset() {
@@ -489,8 +487,6 @@ public class GLStateManager {
modelViewMatrix.clear();
projectionMatrix.clear();
vaoEboMap.clear();
VertexAttribState.reset();
}
/**
@@ -626,18 +622,22 @@ public class GLStateManager {
LOGGER.info("GLStateManager cache bypassed");
}
if (initConfig != null && initConfig.isLwjglDebug()) {
LOGGER.info("Enabling additional LWJGL debug output");
if (RENDER_BACKEND.supportsDebugOutput()) {
LOGGER.info("Enabling additional LWJGL debug output");
GLDebug.setupDebugMessageCallback();
GLDebug.initDebugState();
GLDebug.setupDebugMessageCallback();
GLDebug.initDebugState();
GLDebug.debugMessage("Angelica Debug Annotator Initialized");
GLDebug.debugMessage("Angelica Debug Annotator Initialized");
} else {
LOGGER.info("LWJGL debug output requested but unavailable in this GL context (requires GL 4.3 or GL_KHR_debug); skipping");
}
}
defaultVAO = RENDER_BACKEND.genVertexArrays();
RENDER_BACKEND.bindVertexArray(defaultVAO);
boundVAO = defaultVAO;
VertexAttribState.init(defaultVAO);
VAOManager.init(defaultVAO);
if (initCallback != null) {
initCallback.run();
}
@@ -1015,7 +1015,8 @@ public class GLStateManager {
case GL11.GL_LIST_MODE -> DisplayListManager.getListMode();
case GL11.GL_MATRIX_MODE -> matrixMode.getMode();
case GL11.GL_SHADE_MODEL -> shadeModelState.getValue();
case GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
// GL_TEXTURE_2D makes no sense here, but some mod still queries it...
case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState();
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
@@ -1043,7 +1044,7 @@ public class GLStateManager {
case GL11.GL_STENCIL_CLEAR_VALUE -> stencilState.getClearValue();
case GL15.GL_ARRAY_BUFFER_BINDING -> boundVBO;
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> boundEBO;
case GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING -> VAOManager.boundEBO;
case GL21.GL_PIXEL_UNPACK_BUFFER_BINDING -> boundPixelUnpackBuffer;
case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> boundPixelPackBuffer;
@@ -1192,6 +1193,22 @@ public class GLStateManager {
};
}
private static final FloatBuffer glGetFloatScratch = BufferUtils.createFloatBuffer(16);
public static void glGetFloat(int pname, float[] params) {
final FloatBuffer buf;
if (params.length <= glGetFloatScratch.capacity()) {
buf = glGetFloatScratch;
buf.clear();
buf.limit(params.length);
} else {
buf = BufferUtils.createFloatBuffer(params.length);
}
glGetFloat(pname, buf);
buf.position(0);
buf.get(params);
}
// GLStateManager Functions
public static void glBlendColor(float red, float green, float blue, float alpha) {
@@ -1316,7 +1333,7 @@ public class GLStateManager {
public static void glBlendEquation(int mode) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glBlendEquation in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glBlendEquation");
}
final boolean caching = isCachingEnabled();
final boolean bypass = BYPASS_CACHE || !caching;
@@ -1331,7 +1348,7 @@ public class GLStateManager {
public static void glBlendEquationSeparate(int modeRGB, int modeAlpha) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glBlendEquationSeparate in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glBlendEquationSeparate");
}
final boolean caching = isCachingEnabled();
final boolean bypass = BYPASS_CACHE || !caching;
@@ -1851,7 +1868,7 @@ public class GLStateManager {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromIntBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromIntBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1860,13 +1877,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromFloatBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromFloatBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1875,13 +1893,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromDoubleBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromDoubleBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1890,13 +1909,14 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromByteBuffer(target, level, internalformat, width, height, border, format, type, pixels));
DisplayListManager.recordComplexCommand(TexImage2DCmd.fromByteBuffer(target, level, internalformat, width, height, border, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -1905,15 +1925,17 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, long pixels_buffer_offset) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage2D with buffer offset in display lists not yet supported");
throw DisplayListManager.unsupportedInList("glTexImage2D with buffer offset");
}
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels_buffer_offset);
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels_buffer_offset);
maybeGenerateMipmap(target, level);
}
public static void glTexCoord1f(float s) {
@@ -2195,72 +2217,79 @@ public class GLStateManager {
}
}
public static void preDraw(int drawMode) {
prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
}
public static void preDraw() {
disableWideLineEmulation();
ShaderManager.getInstance().preDraw();
prepareClientArrays();
}
public static void glDrawElements(int mode, ByteBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, IntBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
RENDER_BACKEND.drawElements(mode, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, ShortBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
RENDER_BACKEND.drawElements(mode, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
}
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glDrawElements in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glDrawElements");
}
if (FeedbackManager.isFeedbackMode()) {
FeedbackManager.processDrawElements(mode, count, type, indices);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
} else {
RENDER_BACKEND.drawElements(mode, count, type, indices);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, count, type, indices);
}
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
@@ -2269,7 +2298,7 @@ public class GLStateManager {
if (recordMode != RecordMode.NONE) {
// Core profile: a default VAO is generated at init and glBindVertexArray(0)
// is redirected to it, so a VAO is always bound here — no fallback branches.
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, boundEBO);
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, VAOManager.boundEBO);
if (capture != null) {
DisplayListManager.recordIndexedDrawCapture(capture);
}
@@ -2281,14 +2310,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
} else {
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
return;
}
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
}
@@ -2317,62 +2345,21 @@ public class GLStateManager {
}
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
}
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
preDraw(mode);
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
}
private static void prepareClientArrays() {
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) {
uploadClientArraysToVBO();
if (VAOManager.hasAnyClientSideEnabledAttrib()) {
VAOManager.uploadClientArraysToVBO();
}
}
/**
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
* attribs into a shared stream VBO so the draw succeeds under core profile.
*/
private static void uploadClientArraysToVBO() {
int totalBytes = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
clientArraysVBOOffsets[i] = -1;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled || a.clientPointer == null) continue;
clientArraysVBOOffsets[i] = totalBytes;
totalBytes += a.clientPointer.remaining();
}
if (totalBytes == 0) return;
if (clientArraysVBO == 0) {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = boundVBO;
glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
while (newCap < totalBytes) newCap *= 2;
clientArraysVBOCapacity = newCap;
}
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
if (clientArraysVBOOffsets[i] < 0) continue;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
public static void glDrawArrays(int mode, int first, int count) {
CommandRecorder savedRecorder = null;
final RecordMode recordMode = DisplayListManager.getRecordMode();
@@ -2389,16 +2376,16 @@ public class GLStateManager {
FeedbackManager.processDrawArrays(mode, first, count);
return;
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadsAsTriangles(first, count);
} else if (mode == GL11.GL_QUAD_STRIP) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
} else if (mode == GL11.GL_POLYGON) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
} else {
preDraw(mode);
RENDER_BACKEND.drawArrays(mode, first, count);
}
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
@@ -2545,14 +2532,14 @@ public class GLStateManager {
final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glEnableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap);
if (flag != 0) ShaderManager.getInstance().enableClientVertexFlag(flag);
if (flag != 0) VAOManager.enableClientVertexFlag(flag);
}
public static void glDisableClientState(int cap) {
final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glDisableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap);
if (flag != 0) ShaderManager.getInstance().disableClientVertexFlag(flag);
if (flag != 0) VAOManager.disableClientVertexFlag(flag);
}
public static void glVertexAttrib2f(int index, float v0, float v1) {
@@ -2572,7 +2559,7 @@ public class GLStateManager {
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) {
VertexAttribState.set(index, size, type, normalized, stride, offset, boundVBO);
VAOManager.setAttribute(index, size, type, normalized, stride, offset, boundVBO);
RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
}
@@ -2581,40 +2568,40 @@ public class GLStateManager {
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, DoubleBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
VertexAttribState.set(index, size, type, normalized, stride, pointer, 0);
VAOManager.setAttribute(index, size, type, normalized, stride, pointer);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, long offset) {
VertexAttribState.set(index, size, type, false, stride, offset, boundVBO);
VAOManager.setAttribute(index, size, type, false, stride, offset, boundVBO);
RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, pointer, 0);
VAOManager.setAttribute(index, size, type, false, stride, pointer);
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, IntBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ShortBuffer pointer) {
VertexAttribState.set(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer), 0);
VAOManager.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer));
}
public static void glVertexAttribDivisor(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); }
@@ -2625,12 +2612,12 @@ public class GLStateManager {
public static void glVertexAttribBinding(int attribindex, int bindingindex) { RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); }
public static void glEnableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, true);
VAOManager.enableAttribute(index);
RENDER_BACKEND.enableVertexAttribArray(index);
}
public static void glDisableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, false);
VAOManager.disableAttribute(index);
RENDER_BACKEND.disableVertexAttribArray(index);
}
@@ -2665,7 +2652,7 @@ public class GLStateManager {
public static void glPushClientAttrib(int mask) {
if (clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) {
clientAttribSavedTextureUnit[clientAttribStackPointer] = clientActiveTextureUnit;
clientAttribSavedVertexFlags[clientAttribStackPointer] = ShaderManager.getInstance().getCurrentVertexFlags();
clientAttribSavedVertexFlags[clientAttribStackPointer] = VAOManager.getCurrentVertexFlags();
clientAttribStackPointer++;
}
}
@@ -2674,7 +2661,7 @@ public class GLStateManager {
if (clientAttribStackPointer > 0) {
clientAttribStackPointer--;
clientActiveTextureUnit = clientAttribSavedTextureUnit[clientAttribStackPointer];
ShaderManager.getInstance().setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
VAOManager.setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
}
}
@@ -3451,6 +3438,21 @@ public class GLStateManager {
}
}
public static void setModelViewMatrix(Matrix4fc m) {
if (isCachingEnabled()) {
modelViewMatrix.set(m);
mvGeneration++;
mvLinearGeneration++;
}
}
public static void setProjectionMatrix(Matrix4fc m) {
if (isCachingEnabled()) {
projectionMatrix.set(m);
projGeneration++;
}
}
public static Matrix4fStack getMatrixStack() {
switch (matrixMode.getMode()) {
case GL11.GL_MODELVIEW -> {
@@ -3734,6 +3736,25 @@ public class GLStateManager {
return DisplayListManager.getListMode();
}
private static boolean handleRemovedTexParam(int target, int pname, int value) {
if (pname == GL14.GL_GENERATE_MIPMAP) {
if (target == GL11.GL_TEXTURE_2D) {
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(getBoundTextureForServerState());
if (info != null) info.setGenerateMipmap(value != 0);
}
return true;
}
return false;
}
private static void maybeGenerateMipmap(int target, int level) {
if (target != GL11.GL_TEXTURE_2D) return;
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(getBoundTextureForServerState());
if (info != null && info.isGenerateMipmap() && level == info.getBaseLevel() && level < info.getMaxLevel()) {
RENDER_BACKEND.generateMipmap(target);
}
}
public static boolean updateTexParameteriCache(int target, int texture, int pname, int param) {
if (target != GL11.GL_TEXTURE_2D) {
return true;
@@ -3759,6 +3780,10 @@ public class GLStateManager {
if (info.getWrapT() == param && !shouldBypassCache()) return false;
info.setWrapT(param);
}
case GL12.GL_TEXTURE_BASE_LEVEL -> {
if (info.getBaseLevel() == param && !shouldBypassCache()) return false;
info.setBaseLevel(param);
}
case GL12.GL_TEXTURE_MAX_LEVEL -> {
if (info.getMaxLevel() == param && !shouldBypassCache()) return false;
info.setMaxLevel(param);
@@ -3776,6 +3801,7 @@ public class GLStateManager {
}
public static void glTexParameter(int target, int pname, IntBuffer params) {
if (handleRemovedTexParam(target, pname, params.remaining() >= 1 ? params.get(params.position()) : 0)) return;
if (params.remaining() >= 1) {
final int val = params.get(params.position());
final int remapped = remapTexClamp(pname, val);
@@ -3786,6 +3812,7 @@ public class GLStateManager {
}
public static void glTexParameter(int target, int pname, FloatBuffer params) {
if (handleRemovedTexParam(target, pname, params.remaining() >= 1 ? (int) params.get(params.position()) : 0)) return;
if (params.remaining() >= 1) {
final float val = params.get(params.position());
final float remapped = remapTexClamp(pname, val);
@@ -3796,6 +3823,7 @@ public class GLStateManager {
}
public static void glTexParameteri(int target, int pname, int param) {
if (handleRemovedTexParam(target, pname, param)) return;
param = remapTexClamp(pname, param);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
@@ -3835,6 +3863,7 @@ public class GLStateManager {
}
public static void glTexParameterf(int target, int pname, float param) {
if (handleRemovedTexParam(target, pname, param > 0 ? (int) (param + 0.5f) : (int) (param - 0.5f))) return;
param = remapTexClamp(pname, param);
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
@@ -3867,9 +3896,11 @@ public class GLStateManager {
case GL11.GL_TEXTURE_MAG_FILTER -> info.getMagFilter();
case GL11.GL_TEXTURE_WRAP_S -> info.getWrapS();
case GL11.GL_TEXTURE_WRAP_T -> info.getWrapT();
case GL12.GL_TEXTURE_BASE_LEVEL -> info.getBaseLevel();
case GL12.GL_TEXTURE_MAX_LEVEL -> info.getMaxLevel();
case GL12.GL_TEXTURE_MIN_LOD -> info.getMinLod();
case GL12.GL_TEXTURE_MAX_LOD -> info.getMaxLod();
case GL14.GL_GENERATE_MIPMAP -> info.isGenerateMipmap() ? GL11.GL_TRUE : GL11.GL_FALSE;
default -> {
if (isRecordingDisplayList()) {
throw new IllegalStateException(String.format(
@@ -4405,7 +4436,7 @@ public class GLStateManager {
public static void glTexImage1D(int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage1D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage1D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
@@ -4415,7 +4446,7 @@ public class GLStateManager {
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
@@ -4425,7 +4456,7 @@ public class GLStateManager {
public static void glTexImage3D(int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
internalformat = changeFormatIfDeprecated(internalformat);
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexImage3D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
@@ -4434,7 +4465,7 @@ public class GLStateManager {
public static void glTexSubImage1D(int target, int level, int xoffset, int width, int format, int type, ByteBuffer pixels) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage1D in display lists not yet implemented");
throw DisplayListManager.unsupportedInList("glTexSubImage1D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage1D(target, level, xoffset, width, format, type, pixels);
@@ -4459,9 +4490,14 @@ public class GLStateManager {
}
public static void prepareWideLineEmulation(int drawMode) {
wideLineEmulationActive = wideLineEmulationEnabled
&& (drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
&& lineState.getWidth() > 1.0f;
wideLineEmulationActive =
(drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
&& lineState.getWidth() > 1.0f
&& wideLineEmulationEnabled;
}
public static void disableWideLineEmulation() {
wideLineEmulationActive = false;
}
public static void glFlush() {
@@ -4514,6 +4550,25 @@ public class GLStateManager {
glShaderSource(shader, sb.toString());
}
public static void nglShaderSource(int shader, int count, long strings, long lengths) {
if (count <= 0) return;
final CharSequence source;
if (count == 1) {
source = decodeShaderSourceString(strings, lengths, 0);
} else {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) sb.append(decodeShaderSourceString(strings, lengths, i));
source = sb.toString();
}
glShaderSource(shader, source);
}
private static String decodeShaderSourceString(long strings, long lengths, int i) {
final long strAddr = MemoryUtilities.memGetAddress(strings + (long) i * Pointer.POINTER_SIZE);
final int len = lengths == MemoryUtilities.NULL ? -1 : MemoryUtilities.memGetInt(lengths + (long) i * 4);
return len < 0 ? MemoryUtilities.memUTF8(strAddr) : MemoryUtilities.memUTF8(strAddr, len);
}
/** Check shader type to determine if fragment output transformation is needed. */
private static boolean isFragmentShader(int shader) {
return RENDER_BACKEND.getShaderi(shader, GL20.GL_SHADER_TYPE) == GL20.GL_FRAGMENT_SHADER;
@@ -4523,7 +4578,7 @@ public class GLStateManager {
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromByteBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels));
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromByteBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -4531,12 +4586,13 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromIntBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels));
DisplayListManager.recordComplexCommand(TexSubImage2DCmd.fromIntBuffer(target, level, xoffset, yoffset, width, height, format, type, pixels, pixelUnpackState));
if (mode == RecordMode.COMPILE) {
return;
}
@@ -4544,18 +4600,20 @@ public class GLStateManager {
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
restorePixelUnpackBuffer();
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, long pixels_buffer_offset) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage2D with buffer offset in display lists not yet supported");
throw DisplayListManager.unsupportedInList("glTexSubImage2D with buffer offset");
}
RENDER_BACKEND.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels_buffer_offset);
maybeGenerateMipmap(target, level);
}
public static void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, ByteBuffer pixels) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glTexSubImage3D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glTexSubImage3D");
}
suspendPixelUnpackBuffer();
RENDER_BACKEND.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
@@ -4564,35 +4622,37 @@ public class GLStateManager {
public static void glCopyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexImage1D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexImage1D");
}
RENDER_BACKEND.copyTexImage1D(target, level, internalFormat, x, y, width, border);
}
public static void glCopyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexImage2D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexImage2D");
}
RENDER_BACKEND.copyTexImage2D(target, level, internalFormat, x, y, width, height, border);
maybeGenerateMipmap(target, level);
}
public static void glCopyTexSubImage1D(int target, int level, int xoffset, int x, int y, int width) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage1D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage1D");
}
RENDER_BACKEND.copyTexSubImage1D(target, level, xoffset, x, y, width);
}
public static void glCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage2D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage2D");
}
RENDER_BACKEND.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
maybeGenerateMipmap(target, level);
}
public static void glCopyTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glCopyTexSubImage3D in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glCopyTexSubImage3D");
}
RENDER_BACKEND.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height);
}
@@ -4633,6 +4693,12 @@ public class GLStateManager {
}
public static void glHint(int target, int hint) {
// These hint targets are removed in core profile, ignore them
switch (target) {
case GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_FOG_HINT, GL14.GL_GENERATE_MIPMAP_HINT -> {
return;
}
}
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordHint(target, hint);
@@ -4721,7 +4787,7 @@ public class GLStateManager {
public static void glReadBuffer(int mode) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glReadBuffer in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glReadBuffer");
}
RENDER_BACKEND.readBuffer(mode);
}
@@ -4790,18 +4856,16 @@ public class GLStateManager {
}
}
// glPixelStore* isn'tcompiled into display lists, it executes immediately
public static void glPixelStorei(int pname, int param) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glPixelStorei in display lists not yet implemented - if you see this, please report!");
if (isCachingEnabled()) {
pixelUnpackState = pixelUnpackState.with(pname, param);
}
RENDER_BACKEND.pixelStorei(pname, param);
}
public static void glPixelStoref(int pname, float param) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glPixelStoref in display lists not yet implemented - if you see this, please report!");
}
RENDER_BACKEND.pixelStoref(pname, param);
glPixelStorei(pname, param < 0 ? -Math.round(-param) : Math.round(param));
}
// Display List Commands
@@ -4896,7 +4960,7 @@ public class GLStateManager {
// Multisample Commands
public static void glSampleCoverage(float value, boolean invert) {
if (DisplayListManager.isRecording()) {
throw new UnsupportedOperationException("glSampleCoverage in display lists not yet implemented - if you see this, please report!");
throw DisplayListManager.unsupportedInList("glSampleCoverage");
}
RENDER_BACKEND.sampleCoverage(value, invert);
}
@@ -5021,13 +5085,13 @@ public class GLStateManager {
private static void invalidateDeletedBuffer(int buffer) {
if (buffer == 0) return;
if (boundVBO == buffer) boundVBO = 0;
if (boundEBO == buffer) boundEBO = 0;
if (VAOManager.boundEBO == buffer) VAOManager.boundEBO = 0;
if (boundPixelUnpackBuffer == buffer) boundPixelUnpackBuffer = 0;
if (boundPixelPackBuffer == buffer) boundPixelPackBuffer = 0;
// Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id.
for (Int2IntMap.Entry e : vaoEboMap.int2IntEntrySet()) {
if (e.getIntValue() == buffer) e.setValue(0);
for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
if (data.ebo == buffer) data.ebo = 0;
}
}
@@ -5036,8 +5100,7 @@ public class GLStateManager {
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
boundVBO = buffer;
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
boundEBO = buffer;
vaoEboMap.put(boundVAO, buffer);
VAOManager.onBindEBO(buffer);
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
boundPixelUnpackBuffer = buffer;
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
@@ -5046,6 +5109,14 @@ public class GLStateManager {
RENDER_BACKEND.bindBuffer(target, buffer);
}
public static void forcePixelUnpackState(PixelUnpackState target) {
PixelUnpackState.applyDiff(pixelUnpackState, target);
}
public static void restorePixelUnpackState(PixelUnpackState applied) {
PixelUnpackState.applyDiff(applied, pixelUnpackState);
}
static void suspendPixelUnpackBuffer() {
if (boundPixelUnpackBuffer != 0) {
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
@@ -5113,24 +5184,17 @@ public class GLStateManager {
}
if (boundVAO != array) {
boundVAO = array;
boundEBO = vaoEboMap.get(array);
VertexAttribState.onBindVertexArray(array);
VAOManager.onBindVertexArrayPre(array);
RENDER_BACKEND.bindVertexArray(array);
if (ShaderManager.getInstance().isEnabled()) {
ShaderManager.getInstance().onBindVertexArray(array);
}
}
}
public static void glDeleteVertexArrays(int array) {
ShaderManager.getInstance().onDeleteVertexArray(array);
VertexAttribState.onDeleteVertexArray(array);
vaoEboMap.remove(array);
VAOManager.onDeleteVertexArray(array);
if (array == boundVAO) {
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
boundVAO = defaultVAO;
boundEBO = vaoEboMap.get(defaultVAO);
VertexAttribState.onBindVertexArray(defaultVAO);
VAOManager.onBindVertexArrayPre(defaultVAO);
RENDER_BACKEND.bindVertexArray(defaultVAO);
}
RENDER_BACKEND.deleteVertexArrays(array);
@@ -5140,6 +5204,10 @@ public class GLStateManager {
return RENDER_BACKEND.genVertexArrays();
}
public static boolean glIsVertexArray(int array) {
return RENDER_BACKEND.isVertexArray(array);
}
public static void glBindVertexArrayAPPLE(int array) {
glBindVertexArray(array);
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -9,7 +9,6 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
@@ -17,6 +16,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetShort;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Shared quad-to-triangle EBO for core profile GL_QUADS emulation.
@@ -83,6 +83,7 @@ public final class QuadConverter {
* @param vertexCount number of vertices (must be multiple of 4)
*/
public static void drawQuadsAsTriangles(int first, int vertexCount) {
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;
@@ -106,6 +107,7 @@ public final class QuadConverter {
* @param bytesPerIndex bytes per index element
*/
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO();
@@ -144,7 +146,7 @@ public final class QuadConverter {
final int quadCount = indexCount / 4;
final int triIndexCount = quadCount * 6;
final int bytesPerIndex = VertexAttribState.Attrib.glTypeSizeBytes(type);
final int bytesPerIndex = VAOManager.Attrib.glTypeSizeBytes(type);
final ByteBuffer src = memAlloc(indexCount * bytesPerIndex);
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, src);
@@ -7,7 +7,6 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSACore;
import com.gtnewhorizons.angelica.glsm.dsa.DSAEXT;
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
@@ -507,11 +507,6 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL11.glPixelStorei(pname, param);
}
@Override
public void pixelStoref(int pname, float param) {
GL11.glPixelStoref(pname, param);
}
@Override
public void sampleCoverage(float value, boolean invert) {
GL13.glSampleCoverage(value, invert);
@@ -1043,6 +1038,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
GL30.glBindVertexArray(array);
}
@Override
public boolean isVertexArray(int array) {
return GL30.glIsVertexArray(array);
}
@Override
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
GL20.glVertexAttribPointer(index, size, type, normalized, stride, pointer);
@@ -125,7 +125,6 @@ public abstract class RenderBackend {
public abstract int getTexLevelParameteri(int target, int level, int pname);
public abstract void generateMipmap(int target);
public abstract void pixelStorei(int pname, int param);
public void pixelStoref(int pname, float param) {}
public void sampleCoverage(float value, boolean invert) {}
@@ -247,6 +246,7 @@ public abstract class RenderBackend {
public abstract int genVertexArrays();
public abstract void deleteVertexArrays(int array);
public abstract void bindVertexArray(int array);
public abstract boolean isVertexArray(int array);
public abstract void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer);
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
vertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memAddress0(pointer));
@@ -1,15 +1,13 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.CompatUniformManager;
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.stacks.Vec3fStack;
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import lombok.Getter;
import org.joml.Vector3f;
import org.joml.Vector4f;
@@ -31,7 +29,6 @@ public final class ShaderManager {
private final ShaderCache cache = new ShaderCache();
private final Uniforms uniforms = new Uniforms();
private final Int2IntOpenHashMap vaoVertexFlags = new Int2IntOpenHashMap();
@Getter
private boolean active = false;
@@ -57,10 +54,8 @@ public final class ShaderManager {
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
@Getter private boolean enabled = false;
private int currentVertexFlags = VertexFlags.TEXTURE_BIT | VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT;
private ShaderManager() {
vaoVertexFlags.defaultReturnValue(-1);
if (Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false"))) {
cache.setDumpDir(Paths.get("ffp_shaders"));
}
@@ -73,12 +68,6 @@ public final class ShaderManager {
public void enable() {
enabled = true;
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
currentVertexFlags = format.getVertexFlags();
vaoVertexFlags.put(GLStateManager.getBoundVAO(), currentVertexFlags);
return false;
});
GLStateManager.LOGGER.info("FFP shader emulation enabled");
}
@@ -99,7 +88,7 @@ public final class ShaderManager {
currentFKLen = 0;
}
public void preDraw(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
public void preDraw() {
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null) bh.flushDeferredBlend();
@@ -111,7 +100,12 @@ public final class ShaderManager {
}
}
GLStateManager.flushDeferredVertexAttribs(currentVertexFlags);
final int vertexFlags = VAOManager.getCurrentVertexFlags();
final boolean hasColor = (vertexFlags & VertexFlags.COLOR_BIT) != 0;
final boolean hasNormal = (vertexFlags & VertexFlags.NORMAL_BIT) != 0;
final boolean hasTexCoord = (vertexFlags & VertexFlags.TEXTURE_BIT) != 0;
final boolean hasLightmap = (vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0;
GLStateManager.flushDeferredVertexAttribs(hasColor, hasNormal, hasTexCoord, hasLightmap);
if (!active) return;
@@ -127,20 +121,6 @@ public final class ShaderManager {
uploadUniforms();
}
public void preDraw(int vertexFlags) {
currentVertexFlags = vertexFlags;
preDraw(
(vertexFlags & VertexFlags.COLOR_BIT) != 0,
(vertexFlags & VertexFlags.NORMAL_BIT) != 0,
(vertexFlags & VertexFlags.TEXTURE_BIT) != 0,
(vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0
);
}
public void preDraw() {
preDraw(currentVertexFlags);
}
private void commitVariant(long vkPacked, int fkLen) {
currentVertexKeyPacked = vkPacked;
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
@@ -177,22 +157,6 @@ public final class ShaderManager {
public static void bumpNormalGeneration() { normalGeneration++; }
public static void bumpTexCoordGeneration() { texCoordGeneration++; }
public void enableClientVertexFlag(int flag) {currentVertexFlags |= flag;}
public void disableClientVertexFlag(int flag) {currentVertexFlags &= ~flag;}
public int getCurrentVertexFlags() {return currentVertexFlags;}
public void setCurrentVertexFlags(int flags) {currentVertexFlags = flags;}
public void onBindVertexArray(int vaoId) {
final int flags = vaoVertexFlags.get(vaoId);
if (flags != -1) {
currentVertexFlags = flags;
}
}
public void onDeleteVertexArray(int vaoId) {
vaoVertexFlags.remove(vaoId);
}
public void destroy() {
cache.destroy();
uniforms.destroy();
@@ -0,0 +1,312 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
public final class VAOManager {
public static final int MAX_ATTRIBS = 16;
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
private static VAOData current = null;
private static int currentVertexFlags = 0;
private static Attrib[] currentAttribs = null;
public static int boundEBO = 0;
private static int clientSideEnabledCount = 0;
public static void init(int defaultVAO) {
final VAOData data = new VAOData();
current = data;
currentAttribs = data.attribs;
vaoMap.put(defaultVAO, data);
}
public static void onBindEBO(int ebo) {
boundEBO = ebo;
}
public static void onBindVertexArrayPre(int vaoId) {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.ebo = boundEBO;
current.attribs = currentAttribs;
}
VAOData data = vaoMap.get(vaoId);
if (data == null) {
data = new VAOData();
vaoMap.put(vaoId, data);
}
current = data;
currentVertexFlags = data.vertexFlags;
currentAttribs = data.attribs;
boundEBO = data.ebo;
recomputeClientSideCount();
}
public static void onDeleteVertexArray(int vaoId) {
vaoMap.remove(vaoId);
}
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
public static void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
public static int getCurrentVertexFlags() { return currentVertexFlags; }
public static void setCurrentVertexFlags(int flags) { currentVertexFlags = flags; }
public static final class VAOData {
public Attrib[] attribs;
public int vertexFlags;
public int ebo;
public VAOData() {
attribs = new Attrib[MAX_ATTRIBS];
}
}
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
if (wasClient && a.enabled) {
clientSideEnabledCount--;
}
}
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = 0;
a.clientPointer = pointer;
if (!wasClient && a.enabled) {
clientSideEnabledCount++;
}
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public static void enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && !a.enabled) {
clientSideEnabledCount++;
}
a.enabled = true;
}
public static void disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled) {
clientSideEnabledCount--;
}
a.enabled = false;
}
// Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16.
private static Attrib getAttrib(int index) {
Attrib a = currentAttribs[index];
if (a == null) {
a = new Attrib();
currentAttribs[index] = a;
}
return a;
}
public static boolean isClientSideAttrib(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null && attrib.isClientSide();
}
public static int getAttribEffectiveStride(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.effectiveStride() : 0;
}
public static long getAttribOffset(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.offset : 0;
}
public static int getAttribVBO(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null ? attrib.vboId : 0;
}
public static @Nullable Attrib get(int index) {
return currentAttribs[index];
}
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (isClientSideAttrib(i)) count++;
}
clientSideEnabledCount = count;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
* offsets into a non-existent buffer, causing a native crash at draw time.
*/
public static boolean hasAnyClientSideEnabledAttrib() {
return clientSideEnabledCount > 0;
}
/**
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
* attribs into a shared stream VBO so the draw succeeds under core profile.
*/
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0;
private static final int[] clientArraysVBOOffsets = new int[VAOManager.MAX_ATTRIBS];
public static void uploadClientArraysToVBO() {
int totalBytes = 0;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
clientArraysVBOOffsets[i] = -1;
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled || a.clientPointer == null) continue;
clientArraysVBOOffsets[i] = totalBytes;
totalBytes += a.clientPointer.remaining();
}
if (totalBytes == 0) return;
if (clientArraysVBO == 0) {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = GLStateManager.getBoundVBO();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
while (newCap < totalBytes) newCap *= 2;
clientArraysVBOCapacity = newCap;
}
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
if (clientArraysVBOOffsets[i] < 0) continue;
final VAOManager.Attrib a = VAOManager.get(i);
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
public static final class Attrib {
public boolean enabled;
public int size;
public int type;
public boolean normalized;
public int stride;
public long offset;
public int vboId;
public ByteBuffer clientPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
size = 0;
type = 0;
normalized = false;
stride = 0;
offset = 0;
vboId = 0;
clientPointer = null;
}
public int effectiveStride() {
return (stride != 0) ? stride : size * typeSizeBytes();
}
public int typeSizeBytes() {
return glTypeSizeBytes(type);
}
public static int glTypeSizeBytes(int glType) {
return switch (glType) {
case GL11.GL_FLOAT, GL11.GL_INT, GL11.GL_UNSIGNED_INT -> 4;
case GL11.GL_DOUBLE -> 8;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT -> 2;
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
default -> 4;
};
}
public float readComponent(ByteBuffer buf, int base, int component) {
return readComponent(type, normalized, buf, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
}
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL15;
@@ -15,11 +15,11 @@ public final class AttribSnapshot {
public record AttribDesc(int location, int size, int type, boolean normalized, int stride, long offset, int sourceVboId, ByteBuffer readBuffer, long readBufferBaseOffset) {
public int effectiveStride() {
return stride != 0 ? stride : size * VertexAttribState.Attrib.glTypeSizeBytes(type);
return stride != 0 ? stride : size * VAOManager.Attrib.glTypeSizeBytes(type);
}
public int typeSizeBytes() {
return VertexAttribState.Attrib.glTypeSizeBytes(type);
return VAOManager.Attrib.glTypeSizeBytes(type);
}
}
@@ -56,7 +56,7 @@ public final class AttribSnapshot {
* allocating any hashmaps. A second distinct VBO promotes to the map-backed fallback.
*/
public static AttribSnapshot snapshot(int firstVertex, int vertexCount) {
final AttribDesc[] out = new AttribDesc[VertexAttribState.MAX_ATTRIBS];
final AttribDesc[] out = new AttribDesc[VAOManager.MAX_ATTRIBS];
final List<ByteBuffer> allocated = new ArrayList<>();
final int prevVBO = GLStateManager.getBoundVBO();
@@ -67,11 +67,11 @@ public final class AttribSnapshot {
Int2ObjectOpenHashMap<long[]> vboRanges = null;
final long lastVtx = (long) firstVertex + vertexCount - 1;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled || a.vboId == 0) continue;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled || a.vboId == 0) continue;
final int stride = a.effectiveStride();
final long typeBytes = (long) a.size * VertexAttribState.Attrib.glTypeSizeBytes(a.type);
final long typeBytes = (long) a.size * VAOManager.Attrib.glTypeSizeBytes(a.type);
final long start = a.offset + (long) firstVertex * stride;
final long end = a.offset + lastVtx * stride + typeBytes;
@@ -103,9 +103,9 @@ public final class AttribSnapshot {
Int2LongOpenHashMap vboReadOffsets = null;
try {
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled) continue;
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VAOManager.Attrib a = VAOManager.get(i);
if (a == null || !a.enabled) continue;
if (a.vboId != 0) {
ByteBuffer buf;
@@ -653,12 +653,19 @@ public final class CommandRecorder {
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
c.freeBuffers();
}
memFree(buffer);
for (DisplayListCommand cmd : complexObjects) {
cmd.delete();
}
complexObjects.clear();
if (buffer != null) {
memFree(buffer);
buffer = null;
}
}
/**
* Deletes every allocated object & returns a ByteBuffer with the capacity equal to its limit.
* This CommandRecorder object cannot be used after calling this anymore.
* This CommandRecorder object cannot be used after calling this anymore
*/
public ByteBuffer finish() {
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
@@ -666,7 +673,10 @@ public final class CommandRecorder {
}
final int size = size();
buffer.limit(size);
return memRealloc(buffer, size);
final ByteBuffer out = memRealloc(buffer, size);
buffer = null;
complexObjects.clear();
return out;
}
public DisplayListCommand[] getComplexObjects() {
@@ -5,7 +5,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
@@ -293,7 +293,39 @@ public final class ImmediateModeRecorder {
}
private static float readComponent(AttribReader r, int base, int component) {
return VertexAttribState.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
return VAOManager.Attrib.readComponent(r.type, r.normalized, r.data, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import java.util.Arrays;
@@ -24,7 +24,7 @@ public final class AttribLayoutKey {
int base = 0;
int maxAlign = 1;
for (int i = 0; i < n; i++) {
final int typeSize = VertexAttribState.Attrib.glTypeSizeBytes(types[i]);
final int typeSize = VAOManager.Attrib.glTypeSizeBytes(types[i]);
if (typeSize > maxAlign) maxAlign = typeSize;
base = (base + typeSize - 1) & -typeSize;
off[i] = base;
@@ -3,14 +3,14 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.AttribSnapshot;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
@@ -68,7 +68,7 @@ public final class IndexedDrawCapture {
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
return null;
}
final int srcIndexSize = VertexAttribState.Attrib.glTypeSizeBytes(srcIndexType);
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
if (srcIndexSize <= 0) return null;
final int prevEBO = GLStateManager.getBoundEBO();
@@ -106,7 +106,7 @@ public final class IndexedDrawCapture {
snap = AttribSnapshot.snapshot(minVtx, vertexCount);
int enabledCount = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
if (snap.get(i) != null) enabledCount++;
}
if (enabledCount == 0) {
@@ -118,7 +118,7 @@ public final class IndexedDrawCapture {
final int[] types = new int[enabledCount];
final boolean[] normalized = new boolean[enabledCount];
int k = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final AttribSnapshot.AttribDesc d = snap.get(i);
if (d == null) continue;
locations[k] = i;
@@ -0,0 +1,52 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
final class PixelDataSnapshot {
private PixelDataSnapshot() {}
static @Nullable ByteBuffer copy(@Nullable ByteBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining()).order(ByteOrder.nativeOrder());
out.put(pixels);
out.flip();
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable IntBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
out.asIntBuffer().put(pixels);
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable FloatBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 4).order(ByteOrder.nativeOrder());
out.asFloatBuffer().put(pixels);
pixels.position(pos);
return out;
}
static @Nullable ByteBuffer copy(@Nullable DoubleBuffer pixels) {
if (pixels == null) return null;
final int pos = pixels.position();
final ByteBuffer out = memAlloc(pixels.remaining() * 8).order(ByteOrder.nativeOrder());
out.asDoubleBuffer().put(pixels);
pixels.position(pos);
return out;
}
}
@@ -2,15 +2,17 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
/**
* Command: glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels)
* Specifies a two-dimensional texture image during display list playback.
@@ -24,92 +26,58 @@ public record TexImage2DCmd(
int border,
int format,
int type,
@Nullable byte[] pixelData
@Nullable ByteBuffer pixels,
@Nullable PixelUnpackState unpack
) implements DisplayListCommand {
/**
* Create command from IntBuffer
*/
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per int
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asIntBuffer().put(pixels);
pixels.position(pos); // Restore position
public TexImage2DCmd {
if (pixels == null) {
unpack = null;
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
}
/**
* Create command from FloatBuffer
*/
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per float
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asFloatBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from DoubleBuffer
*/
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 8; // 8 bytes per double
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asDoubleBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from ByteBuffer
*/
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = limit - pos;
data = new byte[size];
pixels.get(data);
pixels.position(pos); // Restore position
}
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, data);
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
@Override
public void execute() {
ByteBuffer buffer = null;
if (pixelData != null) {
buffer = ByteBuffer.allocateDirect(pixelData.length).order(ByteOrder.nativeOrder());
buffer.put(pixelData);
buffer.flip();
if (pixels == null) {
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ByteBuffer) null);
return;
}
GLStateManager.forcePixelUnpackState(unpack);
try {
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
} finally {
GLStateManager.restorePixelUnpackState(unpack);
}
}
@Override
public void delete() {
if (pixels != null) {
memFree(pixels);
}
GLStateManager.glTexImage2D(target, level, internalformat, width, height, border, format, type, buffer);
}
@Override
public @NotNull String toString() {
String dataStr = pixelData != null ? (pixelData.length + " bytes") : "null";
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s)",
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexImage2D(target=%s, level=%d, internalformat=%s, %dx%d, border=%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, GLDebug.getTextureFormatName(internalformat),
width, height, border, GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr);
width, height, border, GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
}
}
@@ -2,15 +2,17 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLDebug;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
/**
* Command: glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels)
* Updates a portion of an existing two-dimensional texture image during display list playback.
@@ -24,92 +26,58 @@ public record TexSubImage2DCmd(
int height,
int format,
int type,
@Nullable byte[] pixelData
@Nullable ByteBuffer pixels,
@Nullable PixelUnpackState unpack
) implements DisplayListCommand {
/**
* Create command from IntBuffer
*/
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per int
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asIntBuffer().put(pixels);
pixels.position(pos); // Restore position
public TexSubImage2DCmd {
if (pixels == null) {
unpack = null;
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
}
/**
* Create command from FloatBuffer
*/
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 4; // 4 bytes per float
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asFloatBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from DoubleBuffer
*/
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = (limit - pos) * 8; // 8 bytes per double
data = new byte[size];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
bb.asDoubleBuffer().put(pixels);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
/**
* Create command from ByteBuffer
*/
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels) {
byte[] data = null;
if (pixels != null) {
int pos = pixels.position();
int limit = pixels.limit();
int size = limit - pos;
data = new byte[size];
pixels.get(data);
pixels.position(pos); // Restore position
}
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, data);
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
}
@Override
public void execute() {
ByteBuffer buffer = null;
if (pixelData != null) {
buffer = ByteBuffer.allocateDirect(pixelData.length).order(ByteOrder.nativeOrder());
buffer.put(pixelData);
buffer.flip();
if (pixels == null) {
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (ByteBuffer) null);
return;
}
GLStateManager.forcePixelUnpackState(unpack);
try {
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
} finally {
GLStateManager.restorePixelUnpackState(unpack);
}
}
@Override
public void delete() {
if (pixels != null) {
memFree(pixels);
}
GLStateManager.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, buffer);
}
@Override
public @NotNull String toString() {
String dataStr = pixelData != null ? (pixelData.length + " bytes") : "null";
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s)",
String dataStr = pixels != null ? (pixels.remaining() + " bytes") : "null";
return String.format("TexSubImage2D(target=%s, level=%d, offset=(%d,%d), %dx%d, format=%s, type=%s, data=%s, unpack=%s)",
GLDebug.getTextureTargetName(target), level, xoffset, yoffset, width, height,
GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr);
GLDebug.getTextureFormatName(format), GLDebug.getDataTypeName(type), dataStr, unpack);
}
}
@@ -126,6 +126,7 @@ public class GLSMRedirector {
.add("glFrustum")
.add("glGetBoolean")
.add("glGetFloat")
.add("glGetFloatv", "glGetFloat")
.add("glGetInteger")
.add("glGetLight")
.add("glGetMaterial")
@@ -296,6 +297,7 @@ public class GLSMRedirector {
.add("glStencilOpSeparate")
.add("glUseProgram")
.add("glShaderSource")
.add("nglShaderSource")
.add("glLinkProgram")
.add("glDeleteProgram")
.add("glCreateShader")
@@ -321,6 +323,7 @@ public class GLSMRedirector {
.add("glUniformMatrix2")
.add("glUniformMatrix3")
.add("glUniformMatrix4")
.add("glUniformMatrix4fv", "glUniformMatrix4")
.add("glDeleteShader")
.add("glGetShaderi")
.add("glGetShaderInfoLog")
@@ -342,6 +345,7 @@ public class GLSMRedirector {
.add("glGenVertexArrays")
.add("glBindVertexArray")
.add("glDeleteVertexArrays")
.add("glIsVertexArray")
.add("glBindFramebuffer")
.add("glDeleteFramebuffers")
.add("glGenFramebuffers")
@@ -587,18 +591,16 @@ public class GLSMRedirector {
}
} else {
final Map<String, String> redirects = mNode.owner.startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(mNode.owner);
if (redirects != null) {
final String glsmName = redirects.get(mNode.name);
if (glsmName != null) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, glsmName, mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = glsmName;
changed = true;
redirectInMethod = true;
final String glsmName = redirects != null ? redirects.get(mNode.name) : null;
if (glsmName != null) {
if (LOG_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, glsmName, mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = glsmName;
changed = true;
redirectInMethod = true;
}
}
// Redirect <init> calls for quadric classes
@@ -617,8 +619,7 @@ public class GLSMRedirector {
for (int i = 0; i < dynNode.bsmArgs.length; i++) {
if (!(dynNode.bsmArgs[i] instanceof Handle handle)) continue;
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
if (redirects == null) continue;
final String glsmName = redirects.get(handle.getName());
final String glsmName = redirects != null ? redirects.get(handle.getName()) : null;
if (glsmName == null) continue;
if (LOG_SPAM) {
final String shortOwner = handle.getOwner().substring(handle.getOwner().lastIndexOf("/") + 1);
@@ -0,0 +1,51 @@
package com.gtnewhorizons.angelica.glsm.states;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/**
* Immutable client-side pixel unpack state (glPixelStore UNPACK_* params)
*/
public record PixelUnpackState(
int alignment,
int rowLength,
int skipRows,
int skipPixels,
int imageHeight,
int skipImages,
boolean swapBytes,
boolean lsbFirst
) {
public static final PixelUnpackState DEFAULT = new PixelUnpackState(4, 0, 0, 0, 0, 0, false, false);
public boolean isDefault() {
return this.equals(DEFAULT);
}
public PixelUnpackState with(int pname, int param) {
return switch (pname) {
case GL11.GL_UNPACK_ALIGNMENT -> (param == 1 || param == 2 || param == 4 || param == 8) ? new PixelUnpackState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_ROW_LENGTH -> param >= 0 ? new PixelUnpackState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SKIP_ROWS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SKIP_PIXELS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst) : this;
case GL12.GL_UNPACK_IMAGE_HEIGHT -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst) : this;
case GL12.GL_UNPACK_SKIP_IMAGES -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst) : this;
case GL11.GL_UNPACK_SWAP_BYTES -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst);
case GL11.GL_UNPACK_LSB_FIRST -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0);
default -> this;
};
}
public static void applyDiff(PixelUnpackState from, PixelUnpackState to) {
if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ALIGNMENT, to.alignment);
if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ROW_LENGTH, to.rowLength);
if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_ROWS, to.skipRows);
if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels);
if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight);
if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages);
if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0);
if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0);
}
}
@@ -1,198 +0,0 @@
package com.gtnewhorizons.angelica.glsm.states;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
/**
* Per-VAO vertex attribute pointer state tracking (Mesa-style).
*/
public class VertexAttribState {
public static final int MAX_ATTRIBS = 16;
private static final Int2ObjectOpenHashMap<Attrib[]> vaoAttribs = new Int2ObjectOpenHashMap<>();
private static Attrib[] current;
private static final ArrayDeque<Attrib[]> pool = new ArrayDeque<>();
private static int clientSideEnabledCount;
public static void init(int defaultVAO) {
vaoAttribs.clear();
pool.clear();
current = allocAttribArray();
vaoAttribs.put(defaultVAO, current);
clientSideEnabledCount = 0;
}
public static void onBindVertexArray(int newVAO) {
current = vaoAttribs.computeIfAbsent(newVAO, k -> allocAttribArray());
recomputeClientSideCount();
}
public static void onDeleteVertexArray(int vaoId) {
final Attrib[] arr = vaoAttribs.remove(vaoId);
if (arr != null) {
pool.addLast(arr);
}
}
public static void reset() {
vaoAttribs.clear();
pool.clear();
current = null;
clientSideEnabledCount = 0;
}
private static Attrib[] allocAttribArray() {
Attrib[] arr = pool.pollFirst();
if (arr != null) {
for (Attrib a : arr) a.reset();
return arr;
}
arr = new Attrib[MAX_ATTRIBS];
for (int i = 0; i < MAX_ATTRIBS; i++) arr[i] = new Attrib();
return arr;
}
public static void set(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
if (was) clientSideEnabledCount--;
}
public static void set(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = vboId;
a.clientPointer = (vboId == 0) ? pointer : null;
final boolean now = a.isClientSide();
if (was != now) clientSideEnabledCount += now ? 1 : -1;
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public static Attrib get(int index) {
return current[index];
}
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (current[i].isClientSide()) count++;
}
clientSideEnabledCount = count;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
* offsets into a non-existent buffer, causing a native crash at draw time.
*/
public static boolean hasAnyClientSideEnabledAttrib() {
return clientSideEnabledCount > 0;
}
public static class Attrib {
public boolean enabled;
public int size;
public int type;
public boolean normalized;
public int stride;
public long offset;
public int vboId;
public ByteBuffer clientPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
size = 0;
type = 0;
normalized = false;
stride = 0;
offset = 0;
vboId = 0;
clientPointer = null;
}
public int effectiveStride() {
return (stride != 0) ? stride : size * typeSizeBytes();
}
public int typeSizeBytes() {
return glTypeSizeBytes(type);
}
public static int glTypeSizeBytes(int glType) {
return switch (glType) {
case GL11.GL_FLOAT, GL11.GL_INT, GL11.GL_UNSIGNED_INT -> 4;
case GL11.GL_DOUBLE -> 8;
case GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT -> 2;
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE -> 1;
default -> 4;
};
}
public float readComponent(ByteBuffer buf, int base, int component) {
return readComponent(type, normalized, buf, base, component);
}
public static float readComponent(int type, boolean normalized, ByteBuffer buf, int base, int component) {
return switch (type) {
case GL11.GL_FLOAT -> buf.getFloat(base + component * 4);
case GL11.GL_DOUBLE -> (float) buf.getDouble(base + component * 8);
case GL11.GL_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? v / (float) Integer.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_INT -> {
final int v = buf.getInt(base + component * 4);
yield normalized ? (v & 0xFFFFFFFFL) / (float) 0xFFFFFFFFL : (float) (v & 0xFFFFFFFFL);
}
case GL11.GL_SHORT -> {
final short v = buf.getShort(base + component * 2);
yield normalized ? v / (float) Short.MAX_VALUE : (float) v;
}
case GL11.GL_UNSIGNED_SHORT -> {
final int v = buf.getShort(base + component * 2) & 0xFFFF;
yield normalized ? v / (float) 0xFFFF : (float) v;
}
case GL11.GL_BYTE -> {
final byte v = buf.get(base + component);
yield normalized ? v / 127.0f : (float) v;
}
case GL11.GL_UNSIGNED_BYTE -> {
final int v = buf.get(base + component) & 0xFF;
yield normalized ? v / 255.0f : (float) v;
}
default -> 0.0f;
};
}
}
}
@@ -7,7 +7,6 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import net.minecraft.client.renderer.Tessellator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -204,14 +203,8 @@ public class TessellatorStreamingDrawer {
firstVertex = 0;
}
GLStateManager.prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw(flags);
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_NORMAL_ARRAY);
}
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
@@ -17,6 +17,7 @@ public class TextureInfo {
@Getter @Setter protected int magFilter = GL11.GL_LINEAR;
@Getter @Setter protected int wrapS = GL11.GL_REPEAT;
@Getter @Setter protected int wrapT = GL11.GL_REPEAT;
@Getter @Setter protected int baseLevel = 0;
@Getter @Setter protected int maxLevel = 1000;
@Getter @Setter protected int minLod = -1000;
@Getter @Setter protected int maxLod = 1000;
@@ -24,6 +25,8 @@ public class TextureInfo {
@Getter @Setter protected float maxAnisotropy = 1.0f;
@Getter @Setter protected boolean generateMipmap = false;
protected TextureInfo(int id) {
this.id = id;
}
@@ -0,0 +1,215 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ExtendWith(GLSMCoreExtension.class)
public class GLSM_DisplayList_PixelStore_Test {
private static final int SRC = 8;
private static final int SUB = 4;
@AfterEach
void resetUnpackState() {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
setUnpack(0, 0, 0);
}
private static void setUnpack(int rowLength, int skipRows, int skipPixels) {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, rowLength);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, skipRows);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, skipPixels);
}
private static void setStridedUnpack() {
setUnpack(SRC, 1, 2);
}
/** RGBA source; red channel = y*size+x, alpha = 255. */
private static ByteBuffer makeSource(int size) {
final ByteBuffer buf = ByteBuffer.allocateDirect(size * size * 4).order(ByteOrder.nativeOrder());
for (int i = 0; i < size * size; i++) {
buf.put((byte) i).put((byte) 0).put((byte) 0).put((byte) 255);
}
buf.flip();
return buf;
}
private static int makeTexture(int size, ByteBuffer pixels) {
final int tex = GLStateManager.glGenTextures();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
return tex;
}
private static ByteBuffer readback(int size) {
final ByteBuffer out = ByteBuffer.allocateDirect(size * size * 4).order(ByteOrder.nativeOrder());
GLStateManager.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, out);
return out;
}
private static int red(ByteBuffer pixels, int size, int x, int y) {
return pixels.get((y * size + x) * 4) & 0xFF;
}
private static void assertStridedSubImage(ByteBuffer pixels) {
for (int y = 0; y < SUB; y++) {
for (int x = 0; x < SUB; x++) {
assertEquals((y + 1) * SRC + (x + 2), red(pixels, SUB, x, y), "pixel (" + x + "," + y + ")");
}
}
}
private static void assertTightSubImage(ByteBuffer pixels) {
for (int y = 0; y < SUB; y++) {
for (int x = 0; x < SUB; x++) {
assertEquals(y * SUB + x, red(pixels, SUB, x, y), "pixel (" + x + "," + y + ")");
}
}
}
private static void assertUnpack(int rowLength, int skipRows, int skipPixels) {
assertEquals(rowLength, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH));
assertEquals(skipRows, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(skipPixels, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_PIXELS));
}
@Test
void pixelStoreExecutesImmediatelyAndIsNotRecorded() {
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 7);
assertEquals(7, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must execute immediately during recording");
GLStateManager.glEndList();
assertEquals(7, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must persist after glEndList");
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
GLStateManager.glCallList(list);
assertEquals(0, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH), "must not have been recorded into the list");
GLStateManager.glDeleteLists(list, 1);
}
@Test
void stridedTexSubImage2DCompile() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
assertEquals(0, red(readback(SUB), SUB, 0, 0), "GL_COMPILE must not execute the upload");
setUnpack(0, 0, 0);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void stridedTexSubImage2DCompileAndExecute() {
final ByteBuffer zeros = ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder());
final int tex = makeTexture(SUB, zeros);
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
assertStridedSubImage(readback(SUB));
setUnpack(0, 0, 0);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, zeros);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void stridedTexImage2DCompile() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
setUnpack(0, 0, 0);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexSubImage2DReplaysWithDefaults() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
setStridedUnpack();
GLStateManager.glCallList(list);
assertTightSubImage(readback(SUB));
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
assertUnpack(SRC, 1, 2);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexImage2DReplaysWithDefaults() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
setStridedUnpack();
GLStateManager.glCallList(list);
assertTightSubImage(readback(SUB));
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
assertUnpack(SRC, 1, 2);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
}
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.util.GLSMUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -133,7 +133,7 @@ public class GLSM_VAO_UnitTest {
private static void glBindVertexArray(int array) {
if (array == 0) array = GLStateManager.getDefaultVAO();
GLStateManager.boundVAO = array;
VertexAttribState.onBindVertexArray(array);
VAOManager.onBindVertexArrayPre(array);
UniversalVAO.bindVertexArray(array);
}
}
@@ -28,7 +28,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testGlColor4fSetsVertexAttrib() {
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.5f, color[0], EPSILON, "color.r");
@@ -40,7 +40,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testGlNormal3fSetsVertexAttrib() {
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
assertEquals(0.0f, normal[0], EPSILON, "normal.x");
@@ -51,7 +51,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() {
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
@@ -64,7 +64,7 @@ class VertexAttribDefaultBridgeTest {
void testGlTexCoord2fSetsVertexAttrib() {
// glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV)
GLStateManager.glTexCoord2f(0.5f, 0.75f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
@@ -77,7 +77,7 @@ class VertexAttribDefaultBridgeTest {
// Second call is a cache hit — changeColor not called.
// Vertex attrib should still hold the correct value from the first call.
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
@@ -89,7 +89,7 @@ class VertexAttribDefaultBridgeTest {
@Test
void testCompileMode_DoesNotSetVertexAttrib() {
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0);
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
int listId = GLStateManager.glGenLists(1);
GLStateManager.glNewList(listId, org.lwjgl.opengl.GL11.GL_COMPILE);
@@ -3,10 +3,10 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.commands.BatchedIndexedDrawCmd;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -307,10 +307,10 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
final ByteBuffer colorSlice = clientData.duplicate().order(ByteOrder.nativeOrder());
colorSlice.position(8);
VertexAttribState.set(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData, 0);
VertexAttribState.setEnabled(0, true);
VertexAttribState.set(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice, 0);
VertexAttribState.setEnabled(1, true);
VAOManager.setAttribute(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData);
VAOManager.enableAttribute(0);
VAOManager.setAttribute(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice);
VAOManager.enableAttribute(1);
GLStateManager.glBindVertexArray(0);
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
@@ -0,0 +1,61 @@
package com.gtnewhorizons.angelica.glsm.states;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PixelUnpackStateTest {
@Test
void defaults() {
assertTrue(PixelUnpackState.DEFAULT.isDefault());
assertEquals(4, PixelUnpackState.DEFAULT.alignment());
assertEquals(0, PixelUnpackState.DEFAULT.rowLength());
assertFalse(PixelUnpackState.DEFAULT.swapBytes());
assertFalse(PixelUnpackState.DEFAULT.lsbFirst());
}
@Test
void withUpdatesUnpackParams() {
PixelUnpackState s = PixelUnpackState.DEFAULT
.with(GL11.GL_UNPACK_ALIGNMENT, 1)
.with(GL11.GL_UNPACK_ROW_LENGTH, 64)
.with(GL11.GL_UNPACK_SKIP_ROWS, 2)
.with(GL11.GL_UNPACK_SKIP_PIXELS, 3)
.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32)
.with(GL12.GL_UNPACK_SKIP_IMAGES, 1)
.with(GL11.GL_UNPACK_SWAP_BYTES, 1)
.with(GL11.GL_UNPACK_LSB_FIRST, 1);
assertEquals(new PixelUnpackState(1, 64, 2, 3, 32, 1, true, true), s);
assertFalse(s.isDefault());
}
@Test
void invalidValuesIgnored() {
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1));
}
@Test
void booleansAcceptAnyNonzero() {
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes());
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst());
PixelUnpackState on = PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1);
assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes());
}
@Test
void packAndUnknownPnamesIgnored() {
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 1));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, 16));
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(0x1234, 1));
}
}
@@ -544,11 +544,6 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
GL11C.glPixelStorei(pname, param);
}
@Override
public void pixelStoref(int pname, float param) {
GL11C.glPixelStoref(pname, param);
}
@Override
public void sampleCoverage(float value, boolean invert) {
GL13C.glSampleCoverage(value, invert);
@@ -1081,6 +1076,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
GL30C.glBindVertexArray(array);
}
@Override
public boolean isVertexArray(int array) {
return GL30C.glIsVertexArray(array);
}
@Override
public void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) {
GL20C.glVertexAttribPointer(index, size, type, normalized, stride, pointer);
@@ -1371,11 +1371,14 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public boolean supportsDebugOutput() {
return caps != null && caps.OpenGL43;
return caps != null && (caps.OpenGL43 || caps.GL_KHR_debug);
}
@Override
public int setupDebugOutput(DebugMessageHandler handler) {
if (caps == null || !(caps.OpenGL43 || caps.GL_KHR_debug)) {
return 0;
}
debugCallback = GLDebugMessageCallback.create(
(source, type, id, severity, length, message, userParam) -> {
final String msg = GLDebugMessageCallback.getMessage(length, message);
@@ -1403,6 +1406,9 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public int disableDebugOutput() {
if (caps == null || !(caps.OpenGL43 || caps.GL_KHR_debug)) {
return 0;
}
GL43C.glDebugMessageCallback(null, 0L);
if (debugCallback != null) {
debugCallback.free();
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.compat;
import com.falsepattern.endlessids.config.GeneralConfig;
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.Loader;
@@ -14,6 +15,8 @@ public class ModStatus {
public static boolean isBetterCrashesLoaded;
public static boolean isNEIDLoaded;
public static boolean isNEIDMetadataExtended;
public static boolean isEIDLoaded;
public static boolean isMetadataExtended;
public static boolean isLotrLoaded;
public static boolean isChunkAPILoaded;
public static boolean isEIDBiomeLoaded;
@@ -35,9 +38,9 @@ public class ModStatus {
isBetterCrashesLoaded = Loader.isModLoaded("bettercrashes");
isNEIDLoaded = Loader.isModLoaded("neid");
isEIDLoaded = Loader.isModLoaded("endlessids");
isLotrLoaded = Loader.isModLoaded("lotr");
isChunkAPILoaded = Loader.isModLoaded("chunkapi");
isEIDBiomeLoaded = Loader.isModLoaded("endlessids_biome");
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
@@ -65,5 +68,15 @@ public class ModStatus {
isNEIDMetadataExtended = true;
}
}
boolean eidBlockItemExtended = false;
boolean eidBiomeExtended = false;
if (isEIDLoaded) {
eidBlockItemExtended = GeneralConfig.extendBlockItem;
eidBiomeExtended = GeneralConfig.extendBiome;
}
isEIDBiomeLoaded = eidBiomeExtended || Loader.isModLoaded("endlessids_biome");
isMetadataExtended = isNEIDMetadataExtended || eidBlockItemExtended;
}
}
@@ -227,13 +227,13 @@ public class AngelicaConfig {
@Config.Comment({"Block corners and edges between chunks might have \"cracks\" (various lines/dots) in them.",
"While using \"Compact Vertex Format\" makes the situation even worse.",
"This option fixes it.",
"Requires texture reloading (F3 + T) after changing this option to take effect"})
@Config.DefaultBoolean(true)
public static boolean fixBlockCrack;
"This option fixes it, though may lead to other visual artifacts.",
"Requires game restart after changing this option to take effect"})
@Config.DefaultBoolean(false)
public static boolean blockCrackFix;
@Config.Comment({
"The \"epsilon\" value for the fixBlockCrack option. ",
"The \"epsilon\" value for the blockCrackFix option. ",
"Set this a bit higher if you can still see lines/dots between solid blocks in dark areas.",
"May cause intense flickering (z-fighting) between blocks if the value is too high"
})
@@ -241,7 +241,7 @@ public class AngelicaConfig {
@Config.DefaultDouble(0.001)
public static double blockCrackFixEpsilon;
@Config.Comment("Block classes that have bugs when rendering with the fixBlockCrack can be put here to avoid manipulating them")
@Config.Comment("Block classes that have bugs when rendering with the blockCrackFix can be put here to avoid manipulating them")
@Config.DefaultStringList({
"net.minecraft.block.BlockCauldron",
"net.minecraft.block.BlockStairs"
@@ -249,7 +249,7 @@ public class AngelicaConfig {
public static String[] blockCrackFixBlacklist;
@Config.Comment({"Block classes that have render pass other than 0 but still need to be manipulated.",
"Add a block class here if you see flickering (z-fighting) with fixBlockCrack enabled"
"Add a block class here if you see flickering (z-fighting) with blockCrackFix enabled"
})
@Config.DefaultStringList({
"gregtech.common.blocks.BlockOres",
@@ -117,7 +117,7 @@ public enum Mixins implements IMixins {
.setPhase(Phase.EARLY)
.addClientMixins("angelica.bugfixes.MixinRenderBlocks_CrackFix")
.addExcludedMod(TargetedMod.FALSETWEAKS)
.setApplyIf(() -> AngelicaConfig.fixBlockCrack)),
.setApplyIf(() -> AngelicaConfig.blockCrackFix)),
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
@@ -14,6 +14,7 @@ import com.gtnewhorizons.angelica.rendering.celeritas.light.VanillaDiffuseProvid
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import lombok.Getter;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.vertices.ExtendedDataHelper;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
@@ -184,6 +185,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
animatedSprites.add(sprite);
}
final boolean isFluid = blockRenderContext.renderType == ExtendedDataHelper.FLUID_RENDER_TYPE;
// If block used vanilla AO, then we can safely compute our own lighting data and ignore what vanilla
// computed. We suppress vanilla emitting the brightness/color in MixinRenderBlocks.
// Otherwise, we need to use the block's lightmap values as they are as the ISBRH may have
@@ -213,6 +216,17 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].trueNormal = trueNormal;
}
if (useAoCalculation && isFluid) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].light = quadLightData.lm[vIdx];
}
}
}
// Apply dynamic lights after AO calculation so they're not overwritten
@@ -7,6 +7,7 @@ import static com.gtnewhorizons.angelica.rendering.celeritas.iris.IrisExtendedCh
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import lombok.Setter;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.vertices.ExtendedDataHelper;
import net.coderbot.iris.vertices.NormalHelper;
@@ -55,7 +56,7 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
public void prepareToRenderBlock(BlockRenderContext ctx, Block block, int metadata, short renderType, byte lightValue) {
this.context = ctx;
Int2IntMap metaMap = blockMetaMatches != null ? blockMetaMatches.get(block) : null;
ctx.blockId = (short) (metaMap != null ? metaMap.get(metadata) : -1);
ctx.blockId = (short) (metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1);
ctx.renderType = renderType;
ctx.lightValue = lightValue;
}
@@ -84,7 +85,7 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
private int lookupFluidMeta(Block block, int metadata) {
final Int2IntMap metaMap = blockMetaMatches.get(block);
return metaMap != null ? metaMap.get(metadata) : -1;
return metaMap != null ? BlockMaterialMapping.resolveId(metaMap, metadata) : -1;
}
private static Block liquidCounterpart(Block block) {
+2 -1
View File
@@ -13,6 +13,7 @@ import cpw.mods.fml.common.gameevent.InputEvent;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import lombok.Getter;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
import net.coderbot.iris.compat.dh.DHCompat;
@@ -948,7 +949,7 @@ public class Iris {
return;
final Int2IntMap metaMap = blockMetaMatches.get(block);
final int blockId = metaMap != null ? metaMap.get(meta) : -1;
final int blockId = metaMap != null ? BlockMaterialMapping.resolveId(metaMap, meta) : -1;
if (TessellatorManager.get() instanceof StateAwareTessellator tess)
tess.angelica$setShaderOverrideBlockId((short) blockId);
@@ -1,5 +1,6 @@
package net.coderbot.iris.block_rendering;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@@ -27,10 +28,12 @@ public class BlockMaterialMapping {
public record BlockIdMaps(Reference2ObjectMap<Block, Int2IntMap> blockMetaMap, NbtConditionalIdMap<Block> tileEntityMap) {}
// Meta-key bits OR'd in at runtime
public static final int SNOWY_META_BIT = 0x10;
public static final int SNOWY_META_BIT = 1 << 16;
public static final int DOUBLE_PLANT_TOP_BIT = 0x8;
public static final int WILDCARD_META_KEY = 1 << 30;
/**
* Creates the standard block meta ID map, the TileEntity NBT-conditional map, and registers
* snowy blocks on {@link BlockRenderingSettings}.
@@ -168,11 +171,17 @@ public class BlockMaterialMapping {
if (metas.isEmpty()) {
for (int meta = 0; meta < 16; meta++) metaMap.putIfAbsent(meta | extraBits, intId);
if (ModStatus.isMetadataExtended) metaMap.putIfAbsent(WILDCARD_META_KEY | extraBits, intId);
} else {
for (int meta : metas) metaMap.putIfAbsent(meta | extraBits, intId);
}
}
public static int resolveId(Int2IntMap metaMap, int metaKey) {
final int id = metaMap.get(metaKey);
return id != -1 ? id : metaMap.get(WILDCARD_META_KEY | (metaKey & SNOWY_META_BIT));
}
/**
* Returns the registered Block for an id, or null if unknown.
* The registry returns Blocks.air (its default) for missing keys, which we coerce to null.
@@ -161,6 +161,7 @@ public class DHCompatInternal {
if (((IRenderTargetExt) Minecraft.getMinecraft().getFramebuffer()).iris$getDepthBufferVersion() != cachedVersion) {
cachedVersion = ((IRenderTargetExt) Minecraft.getMinecraft().getFramebuffer()).iris$getDepthBufferVersion();
createDepthTex(Minecraft.getMinecraft().getFramebuffer().framebufferWidth, Minecraft.getMinecraft().getFramebuffer().framebufferHeight);
storedDepthTex = -1;
}
if (storedDepthTex != depthTex && dhTerrainFramebuffer != null) {
storedDepthTex = depthTex;
@@ -31,7 +31,6 @@ import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
@@ -46,12 +45,6 @@ import java.util.Map;
public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgram {
// Uniforms
public final int modelViewUniform;
public final int modelViewInverseUniform;
public final int projectionUniform;
public final int projectionInverseUniform;
public final int normalMatrix3fUniform;
// Fog/Clip Uniforms
private final int id;
private final ProgramUniforms uniforms;
private final CustomUniforms customUniforms;
@@ -62,7 +55,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
private final Matrix4f tempModel = new Matrix4f();
private final Matrix4f tempProj = new Matrix4f();
private final Matrix4f tempMat4 = new Matrix4f();
private final Matrix3f tempMat3 = new Matrix3f();
private final int instancedShaderOffsetChunkUniform;
private final int instancedShaderOffsetSubChunkUniform;
@@ -143,12 +135,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GLStateManager.glEnableVertexAttribArray(0);
projectionUniform = tryGetUniformLocation2("iris_ProjectionMatrix");
projectionInverseUniform = tryGetUniformLocation2("iris_ProjectionMatrixInverse");
modelViewUniform = tryGetUniformLocation2("iris_ModelViewMatrix");
modelViewInverseUniform = tryGetUniformLocation2("iris_ModelViewMatrixInverse");
normalMatrix3fUniform = tryGetUniformLocation2("iris_NormalMatrix");
this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation2("uOffsetChunk");
this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation2("uOffsetSubChunk");
this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation2("uCameraPosChunk");
@@ -213,18 +199,6 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
}
}
public void setUniform(int index, Matrix3f matrix) {
if (index == -1) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(9);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix3(index, false, buffer);
}
}
// Override ShaderProgram.bind()
public void bind(DhApiRenderParam renderParam) {
GLStateManager.glBindVertexArray(va);
@@ -238,14 +212,8 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
toJOML(tempModel, renderParam.dhModelViewMatrix);
toJOML(tempProj, renderParam.dhProjectionMatrix);
setUniform(modelViewUniform, tempModel);
setUniform(projectionUniform, tempProj);
this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, tempMat4.set(tempProj).mul(tempModel));
tempModel.invert();
tempProj.invert();
setUniform(modelViewInverseUniform, tempModel);
setUniform(projectionInverseUniform, tempProj);
setUniform(normalMatrix3fUniform, tempModel.transpose3x3(tempMat3));
LodRendererEvents.pushDhMatrices(tempProj, tempModel);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + IrisSamplers.LIGHTMAP_TEXTURE_UNIT);
DynamicTexture lightmapTexture = ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture.getGlTextureId());
@@ -259,6 +227,7 @@ public class IrisGenericRenderProgram implements IDhApiGenericObjectShaderProgra
}
public void unbind() {
LodRendererEvents.popDhMatrices();
GLStateManager.glBindVertexArray(0);
GLStateManager.glUseProgram(0);
ProgramUniforms.clearActiveUniforms();
@@ -3,7 +3,6 @@ package net.coderbot.iris.compat.dh;
import com.google.common.primitives.Ints;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor;
import com.mitchej123.lwjgl.MemoryStack;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import net.coderbot.iris.gl.blending.BlendModeOverride;
@@ -26,15 +25,12 @@ import net.coderbot.iris.uniforms.builtin.BuiltinReplacementUniforms;
import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -44,11 +40,6 @@ public class IrisLodRenderProgram {
public final int modelOffsetUniform;
public final int worldYOffsetUniform;
public final int mircoOffsetUniform;
public final int modelViewUniform;
public final int modelViewInverseUniform;
public final int projectionUniform;
public final int projectionInverseUniform;
public final int normalMatrix3fUniform;
// Fog/Clip Uniforms
public final int clipDistanceUniform;
private final int id;
@@ -58,9 +49,6 @@ public class IrisLodRenderProgram {
private final ProgramImages images;
private final BlendModeOverride blend;
private final BufferBlendOverride[] bufferBlendOverrides;
private final Matrix4f tempMat4a = new Matrix4f();
private final Matrix4f tempMat4b = new Matrix4f();
private final Matrix3f tempMat3 = new Matrix3f();
// This will bind AbstractVertexAttribute
private IrisLodRenderProgram(String name, boolean isShadowPass, boolean translucent, BlendModeOverride override, BufferBlendOverride[] bufferBlendOverrides, String vertex, String tessControl, String tessEval, String geometry, String fragment, CustomUniforms customUniforms, DeferredWorldRenderingPipeline pipeline) {
@@ -132,11 +120,6 @@ public class IrisLodRenderProgram {
modelOffsetUniform = tryGetUniformLocation2("modelOffset");
worldYOffsetUniform = tryGetUniformLocation2("worldYOffset");
mircoOffsetUniform = tryGetUniformLocation2("mircoOffset");
projectionUniform = tryGetUniformLocation2("iris_ProjectionMatrix");
projectionInverseUniform = tryGetUniformLocation2("iris_ProjectionMatrixInverse");
modelViewUniform = tryGetUniformLocation2("iris_ModelViewMatrix");
modelViewInverseUniform = tryGetUniformLocation2("iris_ModelViewMatrixInverse");
normalMatrix3fUniform = tryGetUniformLocation2("iris_NormalMatrix");
// Fog/Clip Uniforms
clipDistanceUniform = tryGetUniformLocation2("clipDistance");
@@ -176,30 +159,6 @@ public class IrisLodRenderProgram {
return GLStateManager.glGetUniformLocation(this.id, name);
}
public void setUniform(int index, Matrix4fc matrix) {
if (index == -1 || matrix == null) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(16);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix4(index, false, buffer);
}
}
public void setUniform(int index, Matrix3f matrix) {
if (index == -1) return;
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer buffer = stack.callocFloat(9);
matrix.get(buffer);
buffer.rewind();
GLStateManager.glUniformMatrix3(index, false, buffer);
}
}
// Override ShaderProgram.bind()
public void bind() {
GLStateManager.glUseProgram(id);
@@ -227,11 +186,8 @@ public class IrisLodRenderProgram {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + IrisSamplers.LIGHTMAP_TEXTURE_UNIT);
DynamicTexture lightmapTexture = ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture.getGlTextureId());
setUniform(modelViewUniform, modelView);
setUniform(modelViewInverseUniform, modelView.invert(tempMat4a));
setUniform(projectionUniform, projection);
setUniform(projectionInverseUniform, projection.invert(tempMat4b));
setUniform(normalMatrix3fUniform, tempMat4a.set(modelView).invert().transpose3x3(tempMat3));
LodRendererEvents.pushDhMatrices(projection, modelView);
setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset
@@ -33,6 +33,7 @@ import net.coderbot.iris.shadows.ShadowRenderingState;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.irisshaders.iris.api.v0.IrisApi;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11;
public class LodRendererEvents {
@@ -46,6 +47,28 @@ public class LodRendererEvents {
private static int textureHeight;
private static final Matrix4f tempMat4 = new Matrix4f();
private static boolean dhMatricesActive = false;
private static final Matrix4f savedModelView = new Matrix4f();
private static final Matrix4f savedProjection = new Matrix4f();
static void pushDhMatrices(Matrix4fc projection, Matrix4fc modelView) {
if (!dhMatricesActive) {
savedModelView.set(GLStateManager.getModelViewMatrix());
savedProjection.set(GLStateManager.getProjectionMatrix());
dhMatricesActive = true;
}
GLStateManager.setModelViewMatrix(modelView);
GLStateManager.setProjectionMatrix(projection);
}
static void popDhMatrices() {
if (dhMatricesActive) {
GLStateManager.setModelViewMatrix(savedModelView);
GLStateManager.setProjectionMatrix(savedProjection);
dhMatricesActive = false;
}
}
// constructor //
@@ -181,6 +204,7 @@ public class LodRendererEvents {
DhApiBeforeRenderCleanupEvent beforeCleanupEvent = new DhApiBeforeRenderCleanupEvent() {
@Override
public void beforeCleanup(DhApiEventParam<DhApiRenderParam> event) {
popDhMatrices();
if (getInstance().shouldOverride) {
if (ShadowRenderingState.areShadowsCurrentlyBeingRendered()) {
getInstance().getShadowShader().unbind();
@@ -0,0 +1,21 @@
package net.coderbot.iris.shaderpack;
import java.util.HashMap;
import java.util.Map;
public class DimensionFlatteningMap {
private static final Map<String, String> MODERN_TO_LEGACY = new HashMap<>();
static {
MODERN_TO_LEGACY.put("minecraft:overworld", "Overworld");
MODERN_TO_LEGACY.put("minecraft:the_nether", "Nether");
MODERN_TO_LEGACY.put("minecraft:the_end", "The End");
}
/**
* Maps a modern dimension ID to its 1.7.10 string name.
*/
public static String toLegacyName(String dimensionName) {
return MODERN_TO_LEGACY.getOrDefault(dimensionName, dimensionName);
}
}
@@ -23,14 +23,12 @@ import net.coderbot.iris.shaderpack.option.ShaderPackOptions;
import net.coderbot.iris.shaderpack.option.menu.OptionMenuContainer;
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import net.coderbot.iris.shaderpack.parsing.BooleanParser;
import net.coderbot.iris.shaderpack.preprocessor.JcppProcessor;
import net.coderbot.iris.shaderpack.preprocessor.PropertiesPreprocessor;
import net.coderbot.iris.shaderpack.texture.CustomTextureData;
import net.coderbot.iris.shaderpack.texture.TextureFilteringData;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import net.coderbot.iris.uniforms.custom.CustomUniforms;
import net.irisshaders.iris.api.v0.IrisApi;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
@@ -242,12 +240,8 @@ public class ShaderPack {
List<String> invalidFeatureFlags = invalidFlagList.stream().map(FeatureFlags::getHumanReadableName).collect(Collectors.toList());
if (!invalidFeatureFlags.isEmpty()) {
// TODO: GUI
// if (Minecraft.getMinecraft().screen instanceof ShaderPackScreen) {
// Minecraft.getMinecraft().setScreen(new FeatureMissingErrorScreen(Minecraft.getMinecraft().screen, I18n.format("iris.unsupported.pack"), I18n.format("iris.unsupported.pack.description", FeatureFlags.getInvalidStatus(invalidFlagList), invalidFeatureFlags.stream()
// .collect(Collectors.joining(", ", ": ", ".")))));
// }
IrisApi.getInstance().getConfig().setShadersEnabledAndApply(false);
// TODO: GUI?
Iris.logger.warn("Shader pack requires unsupported feature flags, loading anyway: {}", String.join(", ", invalidFeatureFlags));
}
ProfileSet profiles = ProfileSet.fromTree(shaderProperties.getProfiles(), this.shaderPackOptions.getOptionSet());
@@ -258,9 +252,7 @@ public class ShaderPack {
this.profile.current.ifPresent(profile -> disabledPrograms.addAll(profile.disabledPrograms));
// Add programs that are disabled by shader options
shaderProperties.getConditionallyEnabledPrograms().forEach((program, shaderOption) -> {
if ("true".equals(shaderOption)) return;
if ("false".equals(shaderOption) || !this.shaderPackOptions.getOptionValues().getBooleanValueOrDefault(shaderOption)) {
if (!BooleanParser.parse(shaderOption, this.shaderPackOptions.getOptionValues())) {
disabledPrograms.add(program);
}
});
@@ -438,7 +430,8 @@ public class ShaderPack {
folderNames.add(folderName);
for (String dimensionName : dimensionNames) {
dimensionMap.put(dimensionName, folderName);
// Convert modern dimension IDs to 1.7.10 names
dimensionMap.put(DimensionFlatteningMap.toLegacyName(dimensionName), folderName);
}
});
@@ -668,6 +661,7 @@ public class ShaderPack {
*/
public ProgramSet getProgramSet(String dimensionName) {
int dimensionId = Iris.getCurrentDimensionId();
dimensionName = DimensionFlatteningMap.toLegacyName(dimensionName);
// First, try to find an exact match in the dimension map
String folderName = dimensionMap.get(dimensionName);
@@ -11,6 +11,9 @@ public interface OptionValues {
Optional<String> getStringValue(String name);
default boolean getBooleanValueOrDefault(String name) {
if ("0".equals(name)) return false;
if ("1".equals(name)) return true;
return getBooleanValue(name).orElseGet(() -> {
if (!getOptionSet().getBooleanOptions().containsKey(name)) {
Iris.logger.warn("Tried to get boolean value for unknown option: " + name + ", defaulting to true!");
@@ -0,0 +1,124 @@
package net.coderbot.iris.shaderpack.parsing;
import net.coderbot.iris.Iris;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import java.util.EmptyStackException;
import java.util.Stack;
public class BooleanParser {
public static boolean parse(String expression, OptionValues valueLookup) {
try {
StringBuilder option = new StringBuilder();
Stack<Operation> operationStack = new Stack<>();
Stack<Boolean> valueStack = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
switch (c) {
case '!' -> operationStack.push(Operation.NOT);
case '&' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (operationStack.isEmpty() || !operationStack.peek().equals(Operation.AND)) {
operationStack.push(Operation.OPEN);
}
i++;
operationStack.push(Operation.AND);
}
case '|' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (!operationStack.isEmpty() && operationStack.peek().equals(Operation.AND)) {
evaluate(operationStack, valueStack, true);
}
i++;
operationStack.push(Operation.OR);
}
case '(' -> operationStack.push(Operation.OPEN);
case ')' -> {
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
option = new StringBuilder();
}
if (!operationStack.isEmpty() && operationStack.peek().equals(Operation.AND)) {
evaluate(operationStack, valueStack, true);
}
evaluate(operationStack, valueStack, true);
}
case ' ' -> {}
default -> option.append(c);
}
}
if (!option.isEmpty()) {
valueStack.push(processValue(option.toString(), valueLookup, operationStack));
}
evaluate(operationStack, valueStack, false);
boolean result = valueStack.pop();
if (!valueStack.isEmpty() || !operationStack.isEmpty()) {
Iris.logger.warn("Failed to parse boolean expression, stack isn't empty, defaulting to true: '{}'", expression);
return true;
}
return result;
} catch (EmptyStackException e) {
Iris.logger.warn("Failed to parse boolean expression, stack is empty unexpectedly, defaulting to true: '{}'", expression);
return true;
}
}
private static boolean processValue(String value, OptionValues valueLookup, Stack<Operation> operationStack) {
boolean booleanValue = switch (value) {
case "true", "1" -> true;
case "false", "0" -> false;
default -> valueLookup != null && valueLookup.getBooleanValueOrDefault(value);
};
if (!operationStack.isEmpty() && operationStack.peek() == Operation.NOT) {
operationStack.pop();
return !booleanValue;
}
return booleanValue;
}
private static void evaluate(Stack<Operation> operationStack, Stack<Boolean> valueStack, boolean currentBracket) {
boolean value = valueStack.pop();
while (!operationStack.isEmpty() && (!currentBracket || operationStack.peek() != Operation.OPEN)) {
value = operationStack.pop().compute(value, valueStack);
}
if (!operationStack.isEmpty() && operationStack.peek() == Operation.OPEN) {
operationStack.pop();
if (!operationStack.isEmpty() && operationStack.peek() == Operation.NOT) {
value = operationStack.pop().compute(value, valueStack);
}
}
valueStack.push(value);
}
private enum Operation {
AND {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return valueStack.pop() && value;
}
},
OR {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return valueStack.pop() || value;
}
},
NOT {
@Override
boolean compute(boolean value, Stack<Boolean> valueStack) {
return !value;
}
},
OPEN;
boolean compute(boolean value, Stack<Boolean> valueStack) {
return value;
}
}
}
@@ -116,6 +116,7 @@ public final class CommonUniforms {
final SmoothedVec2f eyeBrightnessSmooth = new SmoothedVec2f(directives.getEyeBrightnessHalfLife(), directives.getEyeBrightnessHalfLife(), CommonUniforms::getEyeBrightness, updateNotifier);
uniforms
.uniform1f(ONCE, "pi", () -> (float) Math.PI)
.uniform1f(ONCE, "darknessFactor", () -> 0.0F) // This is PER_FRAME in modern, it is an effect added by The Warden. We're just setting to 0 because 1.7.10 doesn't have it.
.uniform1f(ONCE, "darknessLightFactor", () -> 0.0F) // Warden darkness current light factor - 1.7.10 doesn't have it so hardcode to 0
.uniform1b(PER_FRAME, "hideGUI", () -> client.gameSettings.hideGUI)
@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntFunction;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import net.coderbot.iris.block_rendering.NbtConditionalIdMap;
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
@@ -96,7 +97,7 @@ public class ItemMaterialHelper {
if (blockMetaMatches != null) {
Int2IntMap metaMap = blockMetaMatches.get(block);
if (metaMap != null) {
int id = metaMap.get(itemBlock.getMetadata(metadata));
int id = BlockMaterialMapping.resolveId(metaMap, itemBlock.getMetadata(metadata));
if (id != -1) {
return id;
}
@@ -72,6 +72,11 @@ public class VanillaBiomeList {
biomes.add(new BiomeEntry(BiomeGenBase.sky, "END_HIGHLANDS"));
biomes.add(new BiomeEntry(BiomeGenBase.sky, "END_BARRENS"));
// Modern biome variants (1.20+)
biomes.add(new BiomeEntry(BiomeGenBase.jungle, "BIOME_SPARSE_JUNGLE"));
biomes.add(new BiomeEntry(BiomeGenBase.mushroomIsland, "BIOME_MUSHROOM_FIELDS"));
biomes.add(new BiomeEntry(BiomeGenBase.swampland, "BIOME_MANGROVE_SWAMP"));
return Collections.unmodifiableList(biomes);
}
@@ -25,7 +25,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* 2. During each glint renderItemIn2D call:
* a. Pre-pass: no color/depth writes, GL_LEQUAL depth test, increment the stencil 1→2
* where depth passes. This determines pixel ownership without corrupting the depth buffer (damn potions...)
* b. Color pass: draw only where stencil == 2, GL_ALWAYS depth test, decrement the stencil
* b. Color pass: draw only where stencil == 2, GL_LEQUAL depth test, decrement the stencil
* 2→1 to reset for the next glint pass.
*
* For the shadow pass, all stencil operations are skipped. Shadow FBOs have no stencil attachment.
@@ -127,7 +127,7 @@ public class MixinItemRenderer_EdgeDepth {
GLStateManager.glColorMask(true, true, true, true);
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 2, 0x03);
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_DECR);
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
original.call(tess, minU, minV, maxU, maxV, w, h, thickness);
// Restore state for the next glint renderItemIn2D call
@@ -254,6 +254,6 @@ public class MixinRenderBlocks_CrackFix {
}
@Unique
private boolean angelica$crackFixOff() {
return !AngelicaConfig.fixBlockCrack || angelica$disableCrackFix;
return !AngelicaConfig.blockCrackFix || angelica$disableCrackFix;
}
}
@@ -193,7 +193,7 @@ public abstract class MixinRenderBlocks {
CompactCtmQuadProcessor processor = ctx.compact().getProcessor();
RenderBlockState renderBlockState = ctx.renderBlockState();
CTMUtils.clearCurrentCompact();
if (this.blockAccess == null) {
if (this.blockAccess == null || renderBlockState.getBlockAccess() == null) {
return;
}
@@ -6,15 +6,12 @@ import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.HandRenderer;
import net.coderbot.iris.uniforms.ItemIdManager;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.item.ItemStack;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
@@ -44,31 +41,6 @@ public class MixinItemRenderer {
ItemIdManager.setItemId(this.itemToRender);
}
/**
* Force the equip lower/raise animation whenever the held stack isn't an exact match for
* the one being rendered.
*/
@Redirect(
method = "updateEquippedItem",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemDamage()I", ordinal = 0)
)
private int iris$animateStackSwap(ItemStack incoming) {
if (this.itemToRender != null
&& !ItemStack.areItemStacksEqual(incoming, this.itemToRender)) {
return Integer.MIN_VALUE;
}
return incoming.getItemDamage();
}
@Redirect(
method = "updateEquippedItem",
at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/ItemRenderer;itemToRender:Lnet/minecraft/item/ItemStack;", opcode = Opcodes.PUTFIELD, ordinal = 0)
)
private void iris$commitSlotOnInstantSwap(ItemRenderer self, ItemStack value) {
this.itemToRender = value;
int equippedItemSlot = Minecraft.getMinecraft().thePlayer.inventory.currentItem;
}
/**
* Reset the item ID after rendering first-person items.
*/
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin allows devs to target the ender crystal beams as a separate entity.
* Mixin allows devs to target the ender crystal beams and dragon death rays as separate entities.
* Also sets the special condition "lightning" on the dragon's death beams.
*/
@Mixin(RenderDragon.class)
@@ -28,9 +28,15 @@ public abstract class MixinRenderDragon {
@Unique
private static final NamespacedId END_CRYSTAL_BEAM = new NamespacedId("minecraft", "end_crystal_beam");
@Unique
private static final NamespacedId DRAGON_DEATH_RAY = new NamespacedId("minecraft", "dragon_death_rays");
@Unique
private int angelica$previousEntityId = -1;
@Unique
private int angelica$previousDeathRayEntityId = -1;
@Unique
private boolean angelica$deathBeamsActive = false;
@@ -83,6 +89,13 @@ public abstract class MixinRenderDragon {
GbufferPrograms.setupSpecialRenderCondition(SpecialCondition.LIGHTNING);
angelica$deathBeamsActive = true;
Object2IntFunction<NamespacedId> entityIdMap = BlockRenderingSettings.INSTANCE.getEntityIds();
if (entityIdMap != null) {
angelica$previousDeathRayEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
int deathRayId = entityIdMap.applyAsInt(DRAGON_DEATH_RAY);
CapturedRenderingState.INSTANCE.setCurrentEntity(deathRayId);
}
angelica$depthPassReplay++;
GLStateManager.glColorMask(false, false, false, false);
angelica$invokeRenderEquippedItems(dragon, partialTicks);
@@ -118,6 +131,10 @@ public abstract class MixinRenderDragon {
private void angelica$endDeathBeamsLighting(EntityDragon dragon, float partialTicks, CallbackInfo ci) {
if (angelica$depthPassReplay > 0) return;
if (angelica$deathBeamsActive) {
if (angelica$previousDeathRayEntityId != -1) {
CapturedRenderingState.INSTANCE.setCurrentEntity(angelica$previousDeathRayEntityId);
angelica$previousDeathRayEntityId = -1;
}
GbufferPrograms.teardownSpecialRenderCondition();
angelica$deathBeamsActive = false;
}