This commit is contained in:
sisyphussy authored and GitHub committed 2026-06-13 17:26:09 -07:00
1 parent 20c3caaed4
commit a7fac16d97
18 files changed
+491 -432

No files matched your search

@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm; package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder; import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import com.gtnewhorizons.angelica.glsm.states.ViewportState; import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector4f; import org.joml.Vector4f;
@@ -9,13 +9,13 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
/** /**
* Software emulation of GL feedback mode * Software emulation of GL feedback mode
*/ */
@@ -127,12 +127,12 @@ public class FeedbackManager {
} }
private static int positionStride() { private static int positionStride() {
final int s = VertexAttribState.get(0).effectiveStride(); final int s = VAOManager.getAttribEffectiveStride(0);
return (s != 0) ? s : 12; return (s != 0) ? s : 12;
} }
private static long positionOffset() { private static long positionOffset() {
return VertexAttribState.get(0).offset; return VAOManager.getAttribOffset(0);
} }
public static void processDrawArrays(int mode, int first, int count) { public static void processDrawArrays(int mode, int first, int count) {
@@ -151,7 +151,7 @@ public class FeedbackManager {
computeMVP(); computeMVP();
final int stride = positionStride(); final int stride = positionStride();
final int elementSize = VertexAttribState.Attrib.glTypeSizeBytes(type); final int elementSize = VAOManager.Attrib.glTypeSizeBytes(type);
final int indexByteCount = indexCount * elementSize; final int indexByteCount = indexCount * elementSize;
ensureIndexReadbackCapacity(indexByteCount); ensureIndexReadbackCapacity(indexByteCount);
indexReadbackBuf.clear().limit(indexByteCount); indexReadbackBuf.clear().limit(indexByteCount);
@@ -255,7 +255,7 @@ public class FeedbackManager {
} }
private static void readPositionBuffer(long offset, ByteBuffer destination) { 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(); final int previousVbo = GLStateManager.getBoundVBO();
if (positionVbo != 0 && positionVbo != previousVbo) { if (positionVbo != 0 && positionVbo != previousVbo) {
@@ -9,6 +9,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode; import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager; import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; 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.DeferredAlphaHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler; import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler; import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
@@ -39,15 +40,12 @@ import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack; import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack; import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState; import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
import com.gtnewhorizons.angelica.glsm.states.Color4; 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.TextureBinding;
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray; 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.TextureInfo;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache; 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.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntStack; import it.unimi.dsi.fastutil.ints.IntStack;
@@ -64,7 +62,6 @@ import org.joml.Vector3f;
import org.joml.Vector4f; import org.joml.Vector4f;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.ARBShaderObjects; import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ContextCapabilities; import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
@@ -99,10 +96,10 @@ import java.util.Set;
import java.util.function.IntSupplier; import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD; 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.INTEL;
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA; import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
import static com.gtnewhorizons.angelica.glsm.Vendor.NVIDIA; 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 * OpenGL State Manager - Provides cached state tracking and management for Backend Renderer Actions
@@ -191,22 +188,22 @@ public class GLStateManager {
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms. // vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
// Skip the backend vertexAttrib call in either case. // Skip the backend vertexAttrib call in either case.
public static void flushDeferredVertexAttribs(int vertexFlags) { public static void flushDeferredVertexAttribs(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
if (dirtyColorAttrib && (vertexFlags & VertexFlags.COLOR_BIT) == 0) { if (!hasColor && dirtyColorAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
dirtyColorAttrib = false; dirtyColorAttrib = false;
} }
if (dirtyNormalAttrib && (vertexFlags & VertexFlags.NORMAL_BIT) == 0) { if (!hasNormal && dirtyNormalAttrib) {
final var n = ShaderManager.getCurrentNormal(); final var n = ShaderManager.getCurrentNormal();
RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z); RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z);
dirtyNormalAttrib = false; dirtyNormalAttrib = false;
} }
if (dirtyTexCoordAttrib && (vertexFlags & VertexFlags.TEXTURE_BIT) == 0) { if (!hasTexCoord && dirtyTexCoordAttrib) {
final var tc = ShaderManager.getCurrentTexCoord(); final var tc = ShaderManager.getCurrentTexCoord();
RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w); RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w);
dirtyTexCoordAttrib = false; dirtyTexCoordAttrib = false;
} }
if (dirtyLightmapAttrib) { if (!hasLightmap && dirtyLightmapAttrib) {
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f); RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
dirtyLightmapAttrib = false; dirtyLightmapAttrib = false;
} }
@@ -458,19 +455,16 @@ public class GLStateManager {
@Getter protected static int listBase = 0; @Getter protected static int listBase = 0;
@Getter protected static int boundVBO; @Getter protected static int boundVBO;
@Getter protected static int boundEBO;
@Getter protected static int boundVAO; @Getter protected static int boundVAO;
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0; public static int getBoundEBO() {
private static final int[] clientArraysVBOOffsets = new int[VertexAttribState.MAX_ATTRIBS]; return VAOManager.boundEBO;
}
private static int boundPixelUnpackBuffer; private static int boundPixelUnpackBuffer;
private static int boundPixelPackBuffer; private static int boundPixelPackBuffer;
private static PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT; private static PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
static {
vaoEboMap.defaultReturnValue(0);
}
@Getter private static int defaultVAO; // Non-zero on core profile @Getter private static int defaultVAO; // Non-zero on core profile
public static void reset() { public static void reset() {
@@ -492,8 +486,6 @@ public class GLStateManager {
modelViewMatrix.clear(); modelViewMatrix.clear();
projectionMatrix.clear(); projectionMatrix.clear();
vaoEboMap.clear();
VertexAttribState.reset();
} }
/** /**
@@ -644,7 +636,7 @@ public class GLStateManager {
defaultVAO = RENDER_BACKEND.genVertexArrays(); defaultVAO = RENDER_BACKEND.genVertexArrays();
RENDER_BACKEND.bindVertexArray(defaultVAO); RENDER_BACKEND.bindVertexArray(defaultVAO);
boundVAO = defaultVAO; boundVAO = defaultVAO;
VertexAttribState.init(defaultVAO); VAOManager.init(defaultVAO);
if (initCallback != null) { if (initCallback != null) {
initCallback.run(); initCallback.run();
} }
@@ -1022,7 +1014,8 @@ public class GLStateManager {
case GL11.GL_LIST_MODE -> DisplayListManager.getListMode(); case GL11.GL_LIST_MODE -> DisplayListManager.getListMode();
case GL11.GL_MATRIX_MODE -> matrixMode.getMode(); case GL11.GL_MATRIX_MODE -> matrixMode.getMode();
case GL11.GL_SHADE_MODEL -> shadeModelState.getValue(); 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_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue(); case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix); case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
@@ -1050,7 +1043,7 @@ public class GLStateManager {
case GL11.GL_STENCIL_CLEAR_VALUE -> stencilState.getClearValue(); case GL11.GL_STENCIL_CLEAR_VALUE -> stencilState.getClearValue();
case GL15.GL_ARRAY_BUFFER_BINDING -> boundVBO; 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_UNPACK_BUFFER_BINDING -> boundPixelUnpackBuffer;
case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> boundPixelPackBuffer; case GL21.GL_PIXEL_PACK_BUFFER_BINDING -> boundPixelPackBuffer;
@@ -2223,6 +2216,18 @@ 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) { public static void glDrawElements(int mode, ByteBuffer indices) {
if (DisplayListManager.isRecording()) { if (DisplayListManager.isRecording()) {
throw DisplayListManager.unsupportedInList("glDrawElements"); throw DisplayListManager.unsupportedInList("glDrawElements");
@@ -2231,9 +2236,7 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices); FeedbackManager.processDrawElements(mode, indices);
return; return;
} }
prepareWideLineEmulation(mode); preDraw(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.drawElements(mode, indices); RENDER_BACKEND.drawElements(mode, indices);
} }
@@ -2245,14 +2248,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices); FeedbackManager.processDrawElements(mode, indices);
return; return;
} }
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) { if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices); QuadConverter.drawQuadElementsAsTriangles(indices);
} else { return;
RENDER_BACKEND.drawElements(mode, indices);
} }
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
} }
public static void glDrawElements(int mode, ShortBuffer indices) { public static void glDrawElements(int mode, ShortBuffer indices) {
@@ -2263,14 +2265,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices); FeedbackManager.processDrawElements(mode, indices);
return; return;
} }
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) { if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices); QuadConverter.drawQuadElementsAsTriangles(indices);
} else { return;
RENDER_BACKEND.drawElements(mode, indices);
} }
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices);
} }
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) { public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
@@ -2281,14 +2282,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, count, type, indices); FeedbackManager.processDrawElements(mode, count, type, indices);
return; return;
} }
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) { if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(count, type, indices); QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
} else { return;
RENDER_BACKEND.drawElements(mode, count, type, indices);
} }
preDraw(mode);
RENDER_BACKEND.drawElements(mode, count, type, indices);
} }
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) { public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
@@ -2297,7 +2297,7 @@ public class GLStateManager {
if (recordMode != RecordMode.NONE) { if (recordMode != RecordMode.NONE) {
// Core profile: a default VAO is generated at init and glBindVertexArray(0) // 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. // 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) { if (capture != null) {
DisplayListManager.recordIndexedDrawCapture(capture); DisplayListManager.recordIndexedDrawCapture(capture);
} }
@@ -2309,14 +2309,13 @@ public class GLStateManager {
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset); FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
return; return;
} }
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) { if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset); QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
} else { return;
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
} }
preDraw(mode);
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder); if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
} }
@@ -2345,62 +2344,21 @@ public class GLStateManager {
} }
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) { public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
ShaderManager.getInstance().preDraw(); preDraw(mode);
prepareClientArrays();
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex); RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
} }
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) { public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
ShaderManager.getInstance().preDraw(); preDraw(mode);
prepareClientArrays();
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount); RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
} }
private static void prepareClientArrays() { private static void prepareClientArrays() {
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) { if (VAOManager.hasAnyClientSideEnabledAttrib()) {
uploadClientArraysToVBO(); 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) { public static void glDrawArrays(int mode, int first, int count) {
CommandRecorder savedRecorder = null; CommandRecorder savedRecorder = null;
final RecordMode recordMode = DisplayListManager.getRecordMode(); final RecordMode recordMode = DisplayListManager.getRecordMode();
@@ -2417,16 +2375,16 @@ public class GLStateManager {
FeedbackManager.processDrawArrays(mode, first, count); FeedbackManager.processDrawArrays(mode, first, count);
return; return;
} }
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) { if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadsAsTriangles(first, count); QuadConverter.drawQuadsAsTriangles(first, count);
} else if (mode == GL11.GL_QUAD_STRIP) { } else if (mode == GL11.GL_QUAD_STRIP) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1); RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_STRIP, first, count & ~1);
} else if (mode == GL11.GL_POLYGON) { } else if (mode == GL11.GL_POLYGON) {
preDraw();
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count); RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
} else { } else {
preDraw(mode);
RENDER_BACKEND.drawArrays(mode, first, count); RENDER_BACKEND.drawArrays(mode, first, count);
} }
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder); if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
@@ -2573,14 +2531,14 @@ public class GLStateManager {
final int location = clientStateToAttributeLocation(cap); final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glEnableVertexAttribArray(location); if (location >= 0) glEnableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap); final int flag = clientStateToVertexFlag(cap);
if (flag != 0) ShaderManager.getInstance().enableClientVertexFlag(flag); if (flag != 0) VAOManager.enableClientVertexFlag(flag);
} }
public static void glDisableClientState(int cap) { public static void glDisableClientState(int cap) {
final int location = clientStateToAttributeLocation(cap); final int location = clientStateToAttributeLocation(cap);
if (location >= 0) glDisableVertexAttribArray(location); if (location >= 0) glDisableVertexAttribArray(location);
final int flag = clientStateToVertexFlag(cap); 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) { public static void glVertexAttrib2f(int index, float v0, float v1) {
@@ -2600,7 +2558,7 @@ public class GLStateManager {
} }
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) { 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); RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset);
} }
@@ -2609,40 +2567,40 @@ public class GLStateManager {
} }
public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) { 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) { 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) { 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) { 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) { 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) { 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); RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset);
} }
public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) { 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) { 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) { 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); } public static void glVertexAttribDivisor(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); }
@@ -2653,12 +2611,12 @@ public class GLStateManager {
public static void glVertexAttribBinding(int attribindex, int bindingindex) { RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); } public static void glVertexAttribBinding(int attribindex, int bindingindex) { RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); }
public static void glEnableVertexAttribArray(int index) { public static void glEnableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, true); VAOManager.enableAttribute(index);
RENDER_BACKEND.enableVertexAttribArray(index); RENDER_BACKEND.enableVertexAttribArray(index);
} }
public static void glDisableVertexAttribArray(int index) { public static void glDisableVertexAttribArray(int index) {
VertexAttribState.setEnabled(index, false); VAOManager.disableAttribute(index);
RENDER_BACKEND.disableVertexAttribArray(index); RENDER_BACKEND.disableVertexAttribArray(index);
} }
@@ -2693,7 +2651,7 @@ public class GLStateManager {
public static void glPushClientAttrib(int mask) { public static void glPushClientAttrib(int mask) {
if (clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) { if (clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) {
clientAttribSavedTextureUnit[clientAttribStackPointer] = clientActiveTextureUnit; clientAttribSavedTextureUnit[clientAttribStackPointer] = clientActiveTextureUnit;
clientAttribSavedVertexFlags[clientAttribStackPointer] = ShaderManager.getInstance().getCurrentVertexFlags(); clientAttribSavedVertexFlags[clientAttribStackPointer] = VAOManager.getCurrentVertexFlags();
clientAttribStackPointer++; clientAttribStackPointer++;
} }
} }
@@ -2702,7 +2660,7 @@ public class GLStateManager {
if (clientAttribStackPointer > 0) { if (clientAttribStackPointer > 0) {
clientAttribStackPointer--; clientAttribStackPointer--;
clientActiveTextureUnit = clientAttribSavedTextureUnit[clientAttribStackPointer]; clientActiveTextureUnit = clientAttribSavedTextureUnit[clientAttribStackPointer];
ShaderManager.getInstance().setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]); VAOManager.setCurrentVertexFlags(clientAttribSavedVertexFlags[clientAttribStackPointer]);
} }
} }
@@ -4516,9 +4474,14 @@ public class GLStateManager {
} }
public static void prepareWideLineEmulation(int drawMode) { public static void prepareWideLineEmulation(int drawMode) {
wideLineEmulationActive = wideLineEmulationEnabled wideLineEmulationActive =
&& (drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP) (drawMode == GL11.GL_LINES || drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP)
&& lineState.getWidth() > 1.0f; && lineState.getWidth() > 1.0f
&& wideLineEmulationEnabled;
}
public static void disableWideLineEmulation() {
wideLineEmulationActive = false;
} }
public static void glFlush() { public static void glFlush() {
@@ -5106,13 +5069,13 @@ public class GLStateManager {
private static void invalidateDeletedBuffer(int buffer) { private static void invalidateDeletedBuffer(int buffer) {
if (buffer == 0) return; if (buffer == 0) return;
if (boundVBO == buffer) boundVBO = 0; if (boundVBO == buffer) boundVBO = 0;
if (boundEBO == buffer) boundEBO = 0; if (VAOManager.boundEBO == buffer) VAOManager.boundEBO = 0;
if (boundPixelUnpackBuffer == buffer) boundPixelUnpackBuffer = 0; if (boundPixelUnpackBuffer == buffer) boundPixelUnpackBuffer = 0;
if (boundPixelPackBuffer == buffer) boundPixelPackBuffer = 0; if (boundPixelPackBuffer == buffer) boundPixelPackBuffer = 0;
// Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id. // Sweep all VAOs: an unbound VAO may later rebind with a dangling EBO id.
for (Int2IntMap.Entry e : vaoEboMap.int2IntEntrySet()) { for (VAOManager.VAOData data : VAOManager.vaoMap.values()) {
if (e.getIntValue() == buffer) e.setValue(0); if (data.ebo == buffer) data.ebo = 0;
} }
} }
@@ -5121,8 +5084,7 @@ public class GLStateManager {
// if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode // if (boundVBO == buffer) return; TODO figure out why this breaks switching async occlusion mode
boundVBO = buffer; boundVBO = buffer;
} else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) { } else if (target == GL15.GL_ELEMENT_ARRAY_BUFFER) {
boundEBO = buffer; VAOManager.onBindEBO(buffer);
vaoEboMap.put(boundVAO, buffer);
} else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) { } else if (target == GL21.GL_PIXEL_UNPACK_BUFFER) {
boundPixelUnpackBuffer = buffer; boundPixelUnpackBuffer = buffer;
} else if (target == GL21.GL_PIXEL_PACK_BUFFER) { } else if (target == GL21.GL_PIXEL_PACK_BUFFER) {
@@ -5206,24 +5168,17 @@ public class GLStateManager {
} }
if (boundVAO != array) { if (boundVAO != array) {
boundVAO = array; boundVAO = array;
boundEBO = vaoEboMap.get(array); VAOManager.onBindVertexArrayPre(array);
VertexAttribState.onBindVertexArray(array);
RENDER_BACKEND.bindVertexArray(array); RENDER_BACKEND.bindVertexArray(array);
if (ShaderManager.getInstance().isEnabled()) {
ShaderManager.getInstance().onBindVertexArray(array);
}
} }
} }
public static void glDeleteVertexArrays(int array) { public static void glDeleteVertexArrays(int array) {
ShaderManager.getInstance().onDeleteVertexArray(array); VAOManager.onDeleteVertexArray(array);
VertexAttribState.onDeleteVertexArray(array);
vaoEboMap.remove(array);
if (array == boundVAO) { if (array == boundVAO) {
// Deleting the bound VAO implicitly unbinds it. Rebind the default VAO. // Deleting the bound VAO implicitly unbinds it. Rebind the default VAO.
boundVAO = defaultVAO; boundVAO = defaultVAO;
boundEBO = vaoEboMap.get(defaultVAO); VAOManager.onBindVertexArrayPre(defaultVAO);
VertexAttribState.onBindVertexArray(defaultVAO);
RENDER_BACKEND.bindVertexArray(defaultVAO); RENDER_BACKEND.bindVertexArray(defaultVAO);
} }
RENDER_BACKEND.deleteVertexArrays(array); RENDER_BACKEND.deleteVertexArrays(array);
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.glsm; 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.GL11;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
@@ -9,7 +9,6 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0; 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.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree; import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte; 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.memGetShort;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt; import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort; 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. * 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) * @param vertexCount number of vertices (must be multiple of 4)
*/ */
public static void drawQuadsAsTriangles(int first, int vertexCount) { public static void drawQuadsAsTriangles(int first, int vertexCount) {
GLStateManager.preDraw();
assert first % 4 == 0 : "QuadConverter: first (" + first + ") must be a multiple of 4"; 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"; assert vertexCount % 4 == 0 : "QuadConverter: vertexCount (" + vertexCount + ") must be a multiple of 4";
final int quadCount = vertexCount / 4; final int quadCount = vertexCount / 4;
@@ -106,6 +107,7 @@ public final class QuadConverter {
* @param bytesPerIndex bytes per index element * @param bytesPerIndex bytes per index element
*/ */
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) { private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
GLStateManager.preDraw();
final int needed = triIndexCount * bytesPerIndex; final int needed = triIndexCount * bytesPerIndex;
final int prevEbo = GLStateManager.getBoundEBO(); final int prevEbo = GLStateManager.getBoundEBO();
@@ -144,7 +146,7 @@ public final class QuadConverter {
final int quadCount = indexCount / 4; final int quadCount = indexCount / 4;
final int triIndexCount = quadCount * 6; 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); final ByteBuffer src = memAlloc(indexCount * bytesPerIndex);
RENDER_BACKEND.getBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, offset, src); 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.DSAEXT;
import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported; import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache; import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -1,15 +1,13 @@
package com.gtnewhorizons.angelica.glsm.ffp; package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags; 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.CompatUniformManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler; import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks; 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.Vec3fStack;
import com.gtnewhorizons.angelica.glsm.stacks.Vec4fStack; 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 lombok.Getter;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector4f; import org.joml.Vector4f;
@@ -31,7 +29,6 @@ public final class ShaderManager {
private final ShaderCache cache = new ShaderCache(); private final ShaderCache cache = new ShaderCache();
private final Uniforms uniforms = new Uniforms(); private final Uniforms uniforms = new Uniforms();
private final Int2IntOpenHashMap vaoVertexFlags = new Int2IntOpenHashMap();
@Getter @Getter
private boolean active = false; private boolean active = false;
@@ -57,10 +54,8 @@ public final class ShaderManager {
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; } public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; } public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
@Getter private boolean enabled = false; @Getter private boolean enabled = false;
private int currentVertexFlags = VertexFlags.TEXTURE_BIT | VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT;
private ShaderManager() { private ShaderManager() {
vaoVertexFlags.defaultReturnValue(-1);
if (Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false"))) { if (Boolean.parseBoolean(System.getProperty("angelica.dumpShaders", "false"))) {
cache.setDumpDir(Paths.get("ffp_shaders")); cache.setDumpDir(Paths.get("ffp_shaders"));
} }
@@ -73,12 +68,6 @@ public final class ShaderManager {
public void enable() { public void enable() {
enabled = true; enabled = true;
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
currentVertexFlags = format.getVertexFlags();
vaoVertexFlags.put(GLStateManager.getBoundVAO(), currentVertexFlags);
return false;
});
GLStateManager.LOGGER.info("FFP shader emulation enabled"); GLStateManager.LOGGER.info("FFP shader emulation enabled");
} }
@@ -99,7 +88,7 @@ public final class ShaderManager {
currentFKLen = 0; currentFKLen = 0;
} }
public void preDraw(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) { public void preDraw() {
final DeferredBlendHandler bh = GLSMHooks.blendHandler; final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null) bh.flushDeferredBlend(); 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; if (!active) return;
@@ -127,20 +121,6 @@ public final class ShaderManager {
uploadUniforms(); 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) { private void commitVariant(long vkPacked, int fkLen) {
currentVertexKeyPacked = vkPacked; currentVertexKeyPacked = vkPacked;
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen); System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
@@ -177,22 +157,6 @@ public final class ShaderManager {
public static void bumpNormalGeneration() { normalGeneration++; } public static void bumpNormalGeneration() { normalGeneration++; }
public static void bumpTexCoordGeneration() { texCoordGeneration++; } 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() { public void destroy() {
cache.destroy(); cache.destroy();
uniforms.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.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager; 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.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL15; 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 record AttribDesc(int location, int size, int type, boolean normalized, int stride, long offset, int sourceVboId, ByteBuffer readBuffer, long readBufferBaseOffset) {
public int effectiveStride() { public int effectiveStride() {
return stride != 0 ? stride : size * VertexAttribState.Attrib.glTypeSizeBytes(type); return stride != 0 ? stride : size * VAOManager.Attrib.glTypeSizeBytes(type);
} }
public int typeSizeBytes() { 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. * allocating any hashmaps. A second distinct VBO promotes to the map-backed fallback.
*/ */
public static AttribSnapshot snapshot(int firstVertex, int vertexCount) { 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 List<ByteBuffer> allocated = new ArrayList<>();
final int prevVBO = GLStateManager.getBoundVBO(); final int prevVBO = GLStateManager.getBoundVBO();
@@ -67,11 +67,11 @@ public final class AttribSnapshot {
Int2ObjectOpenHashMap<long[]> vboRanges = null; Int2ObjectOpenHashMap<long[]> vboRanges = null;
final long lastVtx = (long) firstVertex + vertexCount - 1; final long lastVtx = (long) firstVertex + vertexCount - 1;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) { for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i); final VAOManager.Attrib a = VAOManager.get(i);
if (!a.enabled || a.vboId == 0) continue; if (a == null || !a.enabled || a.vboId == 0) continue;
final int stride = a.effectiveStride(); 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 start = a.offset + (long) firstVertex * stride;
final long end = a.offset + lastVtx * stride + typeBytes; final long end = a.offset + lastVtx * stride + typeBytes;
@@ -103,9 +103,9 @@ public final class AttribSnapshot {
Int2LongOpenHashMap vboReadOffsets = null; Int2LongOpenHashMap vboReadOffsets = null;
try { try {
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) { for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
final VertexAttribState.Attrib a = VertexAttribState.get(i); final VAOManager.Attrib a = VAOManager.get(i);
if (!a.enabled) continue; if (a == null || !a.enabled) continue;
if (a.vboId != 0) { if (a.vboId != 0) {
ByteBuffer buf; ByteBuffer buf;
@@ -5,7 +5,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager; 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 org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -293,7 +293,39 @@ public final class ImmediateModeRecorder {
} }
private static float readComponent(AttribReader r, int base, int component) { 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; 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; import java.util.Arrays;
@@ -24,7 +24,7 @@ public final class AttribLayoutKey {
int base = 0; int base = 0;
int maxAlign = 1; int maxAlign = 1;
for (int i = 0; i < n; i++) { 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; if (typeSize > maxAlign) maxAlign = typeSize;
base = (base + typeSize - 1) & -typeSize; base = (base + typeSize - 1) & -typeSize;
off[i] = base; off[i] = base;
@@ -3,14 +3,14 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities; import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter; 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.recording.AttribSnapshot;
import com.gtnewhorizons.angelica.glsm.states.VertexAttribState;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt; import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong; 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); warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
return null; return null;
} }
final int srcIndexSize = VertexAttribState.Attrib.glTypeSizeBytes(srcIndexType); final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
if (srcIndexSize <= 0) return null; if (srcIndexSize <= 0) return null;
final int prevEBO = GLStateManager.getBoundEBO(); final int prevEBO = GLStateManager.getBoundEBO();
@@ -106,7 +106,7 @@ public final class IndexedDrawCapture {
snap = AttribSnapshot.snapshot(minVtx, vertexCount); snap = AttribSnapshot.snapshot(minVtx, vertexCount);
int enabledCount = 0; 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 (snap.get(i) != null) enabledCount++;
} }
if (enabledCount == 0) { if (enabledCount == 0) {
@@ -118,7 +118,7 @@ public final class IndexedDrawCapture {
final int[] types = new int[enabledCount]; final int[] types = new int[enabledCount];
final boolean[] normalized = new boolean[enabledCount]; final boolean[] normalized = new boolean[enabledCount];
int k = 0; 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); final AttribSnapshot.AttribDesc d = snap.get(i);
if (d == null) continue; if (d == null) continue;
locations[k] = i; locations[k] = i;
@@ -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.GLStateManager;
import com.gtnewhorizons.angelica.glsm.QuadConverter; import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.RenderSystem; import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -204,14 +203,8 @@ public class TessellatorStreamingDrawer {
firstVertex = 0; firstVertex = 0;
} }
GLStateManager.prepareWideLineEmulation(drawMode);
ShaderManager.getInstance().preDraw(flags);
drawWithQuadConversion(drawMode, firstVertex, vertexCount); drawWithQuadConversion(drawMode, firstVertex, vertexCount);
GLStateManager.glBindVertexArray(0); 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) { private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.glsm; package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO; 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 com.gtnewhorizons.angelica.util.GLSMUtil;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -133,7 +133,7 @@ public class GLSM_VAO_UnitTest {
private static void glBindVertexArray(int array) { private static void glBindVertexArray(int array) {
if (array == 0) array = GLStateManager.getDefaultVAO(); if (array == 0) array = GLStateManager.getDefaultVAO();
GLStateManager.boundVAO = array; GLStateManager.boundVAO = array;
VertexAttribState.onBindVertexArray(array); VAOManager.onBindVertexArrayPre(array);
UniversalVAO.bindVertexArray(array); UniversalVAO.bindVertexArray(array);
} }
} }
@@ -28,7 +28,7 @@ class VertexAttribDefaultBridgeTest {
@Test @Test
void testGlColor4fSetsVertexAttrib() { void testGlColor4fSetsVertexAttrib() {
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f); GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB); float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.5f, color[0], EPSILON, "color.r"); assertEquals(0.5f, color[0], EPSILON, "color.r");
@@ -40,7 +40,7 @@ class VertexAttribDefaultBridgeTest {
@Test @Test
void testGlNormal3fSetsVertexAttrib() { void testGlNormal3fSetsVertexAttrib() {
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f); GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB); float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
assertEquals(0.0f, normal[0], EPSILON, "normal.x"); assertEquals(0.0f, normal[0], EPSILON, "normal.x");
@@ -51,7 +51,7 @@ class VertexAttribDefaultBridgeTest {
@Test @Test
void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() { void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() {
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f); GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB); float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s"); assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
@@ -64,7 +64,7 @@ class VertexAttribDefaultBridgeTest {
void testGlTexCoord2fSetsVertexAttrib() { void testGlTexCoord2fSetsVertexAttrib() {
// glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV) // glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV)
GLStateManager.glTexCoord2f(0.5f, 0.75f); GLStateManager.glTexCoord2f(0.5f, 0.75f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB); float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s"); assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
@@ -77,7 +77,7 @@ class VertexAttribDefaultBridgeTest {
// Second call is a cache hit — changeColor not called. // Second call is a cache hit — changeColor not called.
// Vertex attrib should still hold the correct value from the first call. // Vertex attrib should still hold the correct value from the first call.
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f); GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
float[] color = getVertexAttrib4f(COLOR_ATTRIB); float[] color = getVertexAttrib4f(COLOR_ATTRIB);
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup"); assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
@@ -89,7 +89,7 @@ class VertexAttribDefaultBridgeTest {
@Test @Test
void testCompileMode_DoesNotSetVertexAttrib() { void testCompileMode_DoesNotSetVertexAttrib() {
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f); GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
GLStateManager.flushDeferredVertexAttribs(0); GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
int listId = GLStateManager.glGenLists(1); int listId = GLStateManager.glGenLists(1);
GLStateManager.glNewList(listId, org.lwjgl.opengl.GL11.GL_COMPILE); 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.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizons.angelica.glsm.DisplayListManager; import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager; 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.BatchedIndexedDrawCmd;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch; import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture; 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.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
@@ -307,10 +307,10 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
final ByteBuffer colorSlice = clientData.duplicate().order(ByteOrder.nativeOrder()); final ByteBuffer colorSlice = clientData.duplicate().order(ByteOrder.nativeOrder());
colorSlice.position(8); colorSlice.position(8);
VertexAttribState.set(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData, 0); VAOManager.setAttribute(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData);
VertexAttribState.setEnabled(0, true); VAOManager.enableAttribute(0);
VertexAttribState.set(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice, 0); VAOManager.setAttribute(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice);
VertexAttribState.setEnabled(1, true); VAOManager.enableAttribute(1);
GLStateManager.glBindVertexArray(0); GLStateManager.glBindVertexArray(0);
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2); final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
@@ -227,13 +227,13 @@ public class AngelicaConfig {
@Config.Comment({"Block corners and edges between chunks might have \"cracks\" (various lines/dots) in them.", @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.", "While using \"Compact Vertex Format\" makes the situation even worse.",
"This option fixes it.", "This option fixes it, though may lead to other visual artifacts.",
"Requires texture reloading (F3 + T) after changing this option to take effect"}) "Requires game restart after changing this option to take effect"})
@Config.DefaultBoolean(true) @Config.DefaultBoolean(false)
public static boolean fixBlockCrack; public static boolean blockCrackFix;
@Config.Comment({ @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.", "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" "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) @Config.DefaultDouble(0.001)
public static double blockCrackFixEpsilon; 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({ @Config.DefaultStringList({
"net.minecraft.block.BlockCauldron", "net.minecraft.block.BlockCauldron",
"net.minecraft.block.BlockStairs" "net.minecraft.block.BlockStairs"
@@ -249,7 +249,7 @@ public class AngelicaConfig {
public static String[] blockCrackFixBlacklist; public static String[] blockCrackFixBlacklist;
@Config.Comment({"Block classes that have render pass other than 0 but still need to be manipulated.", @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({ @Config.DefaultStringList({
"gregtech.common.blocks.BlockOres", "gregtech.common.blocks.BlockOres",
@@ -117,7 +117,7 @@ public enum Mixins implements IMixins {
.setPhase(Phase.EARLY) .setPhase(Phase.EARLY)
.addClientMixins("angelica.bugfixes.MixinRenderBlocks_CrackFix") .addClientMixins("angelica.bugfixes.MixinRenderBlocks_CrackFix")
.addExcludedMod(TargetedMod.FALSETWEAKS) .addExcludedMod(TargetedMod.FALSETWEAKS)
.setApplyIf(() -> AngelicaConfig.fixBlockCrack)), .setApplyIf(() -> AngelicaConfig.blockCrackFix)),
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN( ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times") new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
@@ -254,6 +254,6 @@ public class MixinRenderBlocks_CrackFix {
} }
@Unique @Unique
private boolean angelica$crackFixOff() { private boolean angelica$crackFixOff() {
return !AngelicaConfig.fixBlockCrack || angelica$disableCrackFix; return !AngelicaConfig.blockCrackFix || angelica$disableCrackFix;
} }
} }