502 lines
15 KiB
Java
502 lines
15 KiB
Java
package com.mojang.blaze3d.systems;
|
|
|
|
import com.mojang.blaze3d.DontObfuscate;
|
|
import com.mojang.blaze3d.ProjectionType;
|
|
import com.mojang.blaze3d.TracyFrameCapture;
|
|
import com.mojang.blaze3d.buffers.BufferType;
|
|
import com.mojang.blaze3d.buffers.BufferUsage;
|
|
import com.mojang.blaze3d.buffers.GpuBuffer;
|
|
import com.mojang.blaze3d.buffers.GpuFence;
|
|
import com.mojang.blaze3d.opengl.GlDevice;
|
|
import com.mojang.blaze3d.platform.GLX;
|
|
import com.mojang.blaze3d.shaders.ShaderType;
|
|
import com.mojang.blaze3d.systems.RenderSystem.AutoStorageIndexBuffer.IndexGenerator;
|
|
import com.mojang.blaze3d.textures.GpuTexture;
|
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
|
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
|
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
|
import com.mojang.blaze3d.vertex.MeshData;
|
|
import com.mojang.blaze3d.vertex.Tesselator;
|
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.IntConsumer;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.api.Environment;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.client.renderer.FogParameters;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.util.ArrayListDeque;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.TimeSource.NanoTimeSource;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.joml.Matrix4f;
|
|
import org.joml.Matrix4fStack;
|
|
import org.joml.Vector3f;
|
|
import org.lwjgl.glfw.GLFW;
|
|
import org.lwjgl.glfw.GLFWErrorCallbackI;
|
|
import org.lwjgl.system.MemoryUtil;
|
|
import org.slf4j.Logger;
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
@DontObfuscate
|
|
public class RenderSystem {
|
|
public static final ScissorState SCISSOR_STATE = new ScissorState();
|
|
static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final int MINIMUM_ATLAS_TEXTURE_SIZE = 1024;
|
|
@Nullable
|
|
private static Thread renderThread;
|
|
@Nullable
|
|
private static GpuDevice DEVICE;
|
|
private static double lastDrawTime = Double.MIN_VALUE;
|
|
private static final RenderSystem.AutoStorageIndexBuffer sharedSequential = new RenderSystem.AutoStorageIndexBuffer(1, 1, IntConsumer::accept);
|
|
private static final RenderSystem.AutoStorageIndexBuffer sharedSequentialQuad = new RenderSystem.AutoStorageIndexBuffer(4, 6, (intConsumer, i) -> {
|
|
intConsumer.accept(i);
|
|
intConsumer.accept(i + 1);
|
|
intConsumer.accept(i + 2);
|
|
intConsumer.accept(i + 2);
|
|
intConsumer.accept(i + 3);
|
|
intConsumer.accept(i);
|
|
});
|
|
private static final RenderSystem.AutoStorageIndexBuffer sharedSequentialLines = new RenderSystem.AutoStorageIndexBuffer(4, 6, (intConsumer, i) -> {
|
|
intConsumer.accept(i);
|
|
intConsumer.accept(i + 1);
|
|
intConsumer.accept(i + 2);
|
|
intConsumer.accept(i + 3);
|
|
intConsumer.accept(i + 2);
|
|
intConsumer.accept(i + 1);
|
|
});
|
|
private static Matrix4f projectionMatrix = new Matrix4f();
|
|
private static Matrix4f savedProjectionMatrix = new Matrix4f();
|
|
private static ProjectionType projectionType = ProjectionType.PERSPECTIVE;
|
|
private static ProjectionType savedProjectionType = ProjectionType.PERSPECTIVE;
|
|
private static final Matrix4fStack modelViewStack = new Matrix4fStack(16);
|
|
private static Matrix4f textureMatrix = new Matrix4f();
|
|
public static final int TEXTURE_COUNT = 12;
|
|
private static final GpuTexture[] shaderTextures = new GpuTexture[12];
|
|
private static final float[] shaderColor = new float[]{1.0F, 1.0F, 1.0F, 1.0F};
|
|
private static float shaderGlintAlpha = 1.0F;
|
|
private static FogParameters shaderFog = FogParameters.NO_FOG;
|
|
private static final Vector3f[] shaderLightDirections = new Vector3f[2];
|
|
private static float shaderGameTime;
|
|
private static final Vector3f modelOffset = new Vector3f();
|
|
private static float shaderLineWidth = 1.0F;
|
|
private static String apiDescription = "Unknown";
|
|
private static final AtomicLong pollEventsWaitStart = new AtomicLong();
|
|
private static final AtomicBoolean pollingEvents = new AtomicBoolean(false);
|
|
@Nullable
|
|
private static GpuBuffer QUAD_VERTEX_BUFFER;
|
|
private static final ArrayListDeque<RenderSystem.GpuAsyncTask> PENDING_FENCES = new ArrayListDeque<>();
|
|
|
|
public static void initRenderThread() {
|
|
if (renderThread != null) {
|
|
throw new IllegalStateException("Could not initialize render thread");
|
|
} else {
|
|
renderThread = Thread.currentThread();
|
|
}
|
|
}
|
|
|
|
public static boolean isOnRenderThread() {
|
|
return Thread.currentThread() == renderThread;
|
|
}
|
|
|
|
public static void assertOnRenderThread() {
|
|
if (!isOnRenderThread()) {
|
|
throw constructThreadException();
|
|
}
|
|
}
|
|
|
|
private static IllegalStateException constructThreadException() {
|
|
return new IllegalStateException("Rendersystem called from wrong thread");
|
|
}
|
|
|
|
private static void pollEvents() {
|
|
pollEventsWaitStart.set(Util.getMillis());
|
|
pollingEvents.set(true);
|
|
GLFW.glfwPollEvents();
|
|
pollingEvents.set(false);
|
|
}
|
|
|
|
public static boolean isFrozenAtPollEvents() {
|
|
return pollingEvents.get() && Util.getMillis() - pollEventsWaitStart.get() > 200L;
|
|
}
|
|
|
|
public static void flipFrame(long l, @Nullable TracyFrameCapture tracyFrameCapture) {
|
|
pollEvents();
|
|
Tesselator.getInstance().clear();
|
|
GLFW.glfwSwapBuffers(l);
|
|
if (tracyFrameCapture != null) {
|
|
tracyFrameCapture.endFrame();
|
|
}
|
|
|
|
pollEvents();
|
|
}
|
|
|
|
public static void limitDisplayFPS(int i) {
|
|
double d = lastDrawTime + 1.0 / i;
|
|
|
|
double e;
|
|
for (e = GLFW.glfwGetTime(); e < d; e = GLFW.glfwGetTime()) {
|
|
GLFW.glfwWaitEventsTimeout(d - e);
|
|
}
|
|
|
|
lastDrawTime = e;
|
|
}
|
|
|
|
public static void enableScissor(int i, int j, int k, int l) {
|
|
SCISSOR_STATE.enable(i, j, k, l);
|
|
}
|
|
|
|
public static void disableScissor() {
|
|
SCISSOR_STATE.disable();
|
|
}
|
|
|
|
public static void setShaderFog(FogParameters fogParameters) {
|
|
assertOnRenderThread();
|
|
shaderFog = fogParameters;
|
|
}
|
|
|
|
public static FogParameters getShaderFog() {
|
|
assertOnRenderThread();
|
|
return shaderFog;
|
|
}
|
|
|
|
public static void setShaderGlintAlpha(double d) {
|
|
setShaderGlintAlpha((float)d);
|
|
}
|
|
|
|
public static void setShaderGlintAlpha(float f) {
|
|
assertOnRenderThread();
|
|
shaderGlintAlpha = f;
|
|
}
|
|
|
|
public static float getShaderGlintAlpha() {
|
|
assertOnRenderThread();
|
|
return shaderGlintAlpha;
|
|
}
|
|
|
|
public static void setShaderLights(Vector3f vector3f, Vector3f vector3f2) {
|
|
assertOnRenderThread();
|
|
shaderLightDirections[0] = vector3f;
|
|
shaderLightDirections[1] = vector3f2;
|
|
}
|
|
|
|
public static Vector3f[] getShaderLights() {
|
|
return shaderLightDirections;
|
|
}
|
|
|
|
public static void setShaderColor(float f, float g, float h, float i) {
|
|
assertOnRenderThread();
|
|
shaderColor[0] = f;
|
|
shaderColor[1] = g;
|
|
shaderColor[2] = h;
|
|
shaderColor[3] = i;
|
|
}
|
|
|
|
public static float[] getShaderColor() {
|
|
assertOnRenderThread();
|
|
return shaderColor;
|
|
}
|
|
|
|
public static void lineWidth(float f) {
|
|
assertOnRenderThread();
|
|
shaderLineWidth = f;
|
|
}
|
|
|
|
public static float getShaderLineWidth() {
|
|
assertOnRenderThread();
|
|
return shaderLineWidth;
|
|
}
|
|
|
|
public static String getBackendDescription() {
|
|
return String.format(Locale.ROOT, "LWJGL version %s", GLX._getLWJGLVersion());
|
|
}
|
|
|
|
public static String getApiDescription() {
|
|
return apiDescription;
|
|
}
|
|
|
|
public static NanoTimeSource initBackendSystem() {
|
|
return GLX._initGlfw()::getAsLong;
|
|
}
|
|
|
|
public static void initRenderer(long l, int i, boolean bl, BiFunction<ResourceLocation, ShaderType, String> biFunction, boolean bl2) {
|
|
DEVICE = new GlDevice(l, i, bl, biFunction, bl2);
|
|
apiDescription = getDevice().getImplementationInformation();
|
|
|
|
try (ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(DefaultVertexFormat.POSITION.getVertexSize() * 4)) {
|
|
BufferBuilder bufferBuilder = new BufferBuilder(byteBufferBuilder, VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION);
|
|
bufferBuilder.addVertex(0.0F, 0.0F, 0.0F);
|
|
bufferBuilder.addVertex(1.0F, 0.0F, 0.0F);
|
|
bufferBuilder.addVertex(1.0F, 1.0F, 0.0F);
|
|
bufferBuilder.addVertex(0.0F, 1.0F, 0.0F);
|
|
|
|
try (MeshData meshData = bufferBuilder.buildOrThrow()) {
|
|
QUAD_VERTEX_BUFFER = getDevice().createBuffer(() -> "Quad", BufferType.VERTICES, BufferUsage.STATIC_WRITE, meshData.vertexBuffer());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void setErrorCallback(GLFWErrorCallbackI gLFWErrorCallbackI) {
|
|
GLX._setGlfwErrorCallback(gLFWErrorCallbackI);
|
|
}
|
|
|
|
public static void setupDefaultState() {
|
|
projectionMatrix.identity();
|
|
savedProjectionMatrix.identity();
|
|
modelViewStack.clear();
|
|
textureMatrix.identity();
|
|
}
|
|
|
|
public static void setupOverlayColor(@Nullable GpuTexture gpuTexture) {
|
|
assertOnRenderThread();
|
|
setShaderTexture(1, gpuTexture);
|
|
}
|
|
|
|
public static void teardownOverlayColor() {
|
|
assertOnRenderThread();
|
|
setShaderTexture(1, null);
|
|
}
|
|
|
|
public static void setupLevelDiffuseLighting(Vector3f vector3f, Vector3f vector3f2) {
|
|
assertOnRenderThread();
|
|
setShaderLights(vector3f, vector3f2);
|
|
}
|
|
|
|
public static void setupGuiFlatDiffuseLighting(Vector3f vector3f, Vector3f vector3f2) {
|
|
assertOnRenderThread();
|
|
Matrix4f matrix4f = new Matrix4f().rotationY((float) (-Math.PI / 8)).rotateX((float) (Math.PI * 3.0 / 4.0));
|
|
setShaderLights(matrix4f.transformDirection(vector3f, new Vector3f()), matrix4f.transformDirection(vector3f2, new Vector3f()));
|
|
}
|
|
|
|
public static void setupGui3DDiffuseLighting(Vector3f vector3f, Vector3f vector3f2) {
|
|
assertOnRenderThread();
|
|
Matrix4f matrix4f = new Matrix4f()
|
|
.scaling(1.0F, -1.0F, 1.0F)
|
|
.rotateYXZ(1.0821041F, 3.2375858F, 0.0F)
|
|
.rotateYXZ((float) (-Math.PI / 8), (float) (Math.PI * 3.0 / 4.0), 0.0F);
|
|
setShaderLights(matrix4f.transformDirection(vector3f, new Vector3f()), matrix4f.transformDirection(vector3f2, new Vector3f()));
|
|
}
|
|
|
|
public static void setShaderTexture(int i, @Nullable GpuTexture gpuTexture) {
|
|
assertOnRenderThread();
|
|
if (i >= 0 && i < shaderTextures.length) {
|
|
shaderTextures[i] = gpuTexture;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public static GpuTexture getShaderTexture(int i) {
|
|
assertOnRenderThread();
|
|
return i >= 0 && i < shaderTextures.length ? shaderTextures[i] : null;
|
|
}
|
|
|
|
public static void setProjectionMatrix(Matrix4f matrix4f, ProjectionType projectionType) {
|
|
assertOnRenderThread();
|
|
projectionMatrix = new Matrix4f(matrix4f);
|
|
RenderSystem.projectionType = projectionType;
|
|
}
|
|
|
|
public static void setTextureMatrix(Matrix4f matrix4f) {
|
|
assertOnRenderThread();
|
|
textureMatrix = new Matrix4f(matrix4f);
|
|
}
|
|
|
|
public static void resetTextureMatrix() {
|
|
assertOnRenderThread();
|
|
textureMatrix.identity();
|
|
}
|
|
|
|
public static void backupProjectionMatrix() {
|
|
assertOnRenderThread();
|
|
savedProjectionMatrix = projectionMatrix;
|
|
savedProjectionType = projectionType;
|
|
}
|
|
|
|
public static void restoreProjectionMatrix() {
|
|
assertOnRenderThread();
|
|
projectionMatrix = savedProjectionMatrix;
|
|
projectionType = savedProjectionType;
|
|
}
|
|
|
|
public static Matrix4f getProjectionMatrix() {
|
|
assertOnRenderThread();
|
|
return projectionMatrix;
|
|
}
|
|
|
|
public static Matrix4f getModelViewMatrix() {
|
|
assertOnRenderThread();
|
|
return modelViewStack;
|
|
}
|
|
|
|
public static Matrix4fStack getModelViewStack() {
|
|
assertOnRenderThread();
|
|
return modelViewStack;
|
|
}
|
|
|
|
public static Matrix4f getTextureMatrix() {
|
|
assertOnRenderThread();
|
|
return textureMatrix;
|
|
}
|
|
|
|
public static RenderSystem.AutoStorageIndexBuffer getSequentialBuffer(VertexFormat.Mode mode) {
|
|
assertOnRenderThread();
|
|
|
|
return switch (mode) {
|
|
case QUADS -> sharedSequentialQuad;
|
|
case LINES -> sharedSequentialLines;
|
|
default -> sharedSequential;
|
|
};
|
|
}
|
|
|
|
public static void setShaderGameTime(long l, float f) {
|
|
assertOnRenderThread();
|
|
shaderGameTime = ((float)(l % 24000L) + f) / 24000.0F;
|
|
}
|
|
|
|
public static float getShaderGameTime() {
|
|
assertOnRenderThread();
|
|
return shaderGameTime;
|
|
}
|
|
|
|
public static ProjectionType getProjectionType() {
|
|
assertOnRenderThread();
|
|
return projectionType;
|
|
}
|
|
|
|
public static GpuBuffer getQuadVertexBuffer() {
|
|
if (QUAD_VERTEX_BUFFER == null) {
|
|
throw new IllegalStateException("Can't getQuadVertexBuffer() before renderer was initialized");
|
|
} else {
|
|
return QUAD_VERTEX_BUFFER;
|
|
}
|
|
}
|
|
|
|
public static void setModelOffset(float f, float g, float h) {
|
|
assertOnRenderThread();
|
|
modelOffset.set(f, g, h);
|
|
}
|
|
|
|
public static void resetModelOffset() {
|
|
assertOnRenderThread();
|
|
modelOffset.set(0.0F, 0.0F, 0.0F);
|
|
}
|
|
|
|
public static Vector3f getModelOffset() {
|
|
assertOnRenderThread();
|
|
return modelOffset;
|
|
}
|
|
|
|
public static void queueFencedTask(Runnable runnable) {
|
|
PENDING_FENCES.addLast(new RenderSystem.GpuAsyncTask(runnable, new GpuFence()));
|
|
}
|
|
|
|
public static void executePendingTasks() {
|
|
for (RenderSystem.GpuAsyncTask gpuAsyncTask = PENDING_FENCES.peekFirst(); gpuAsyncTask != null; gpuAsyncTask = PENDING_FENCES.peekFirst()) {
|
|
if (!gpuAsyncTask.fence.awaitCompletion(0L)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
gpuAsyncTask.callback.run();
|
|
} finally {
|
|
gpuAsyncTask.fence.close();
|
|
}
|
|
|
|
PENDING_FENCES.removeFirst();
|
|
}
|
|
}
|
|
|
|
public static GpuDevice getDevice() {
|
|
if (DEVICE == null) {
|
|
throw new IllegalStateException("Can't getDevice() before it was initialized");
|
|
} else {
|
|
return DEVICE;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public static GpuDevice tryGetDevice() {
|
|
return DEVICE;
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
public static final class AutoStorageIndexBuffer {
|
|
private final int vertexStride;
|
|
private final int indexStride;
|
|
private final IndexGenerator generator;
|
|
@Nullable
|
|
private GpuBuffer buffer;
|
|
private VertexFormat.IndexType type = VertexFormat.IndexType.SHORT;
|
|
private int indexCount;
|
|
|
|
AutoStorageIndexBuffer(int vertexStride, int indexStride, IndexGenerator generator) {
|
|
this.vertexStride = vertexStride;
|
|
this.indexStride = indexStride;
|
|
this.generator = generator;
|
|
}
|
|
|
|
public boolean hasStorage(int index) {
|
|
return index <= this.indexCount;
|
|
}
|
|
|
|
public GpuBuffer getBuffer(int index) {
|
|
this.ensureStorage(index);
|
|
return this.buffer;
|
|
}
|
|
|
|
private void ensureStorage(int neededIndexCount) {
|
|
if (!this.hasStorage(neededIndexCount)) {
|
|
neededIndexCount = Mth.roundToward(neededIndexCount * 2, this.indexStride);
|
|
RenderSystem.LOGGER.debug("Growing IndexBuffer: Old limit {}, new limit {}.", this.indexCount, neededIndexCount);
|
|
int i = neededIndexCount / this.indexStride;
|
|
int j = i * this.vertexStride;
|
|
VertexFormat.IndexType indexType = VertexFormat.IndexType.least(j);
|
|
int k = Mth.roundToward(neededIndexCount * indexType.bytes, 4);
|
|
ByteBuffer byteBuffer = MemoryUtil.memAlloc(k);
|
|
|
|
try {
|
|
this.type = indexType;
|
|
it.unimi.dsi.fastutil.ints.IntConsumer intConsumer = this.intConsumer(byteBuffer);
|
|
|
|
for (int l = 0; l < neededIndexCount; l += this.indexStride) {
|
|
this.generator.accept(intConsumer, l * this.vertexStride / this.indexStride);
|
|
}
|
|
|
|
byteBuffer.flip();
|
|
if (this.buffer != null) {
|
|
this.buffer.close();
|
|
}
|
|
|
|
this.buffer = RenderSystem.getDevice().createBuffer(() -> "Auto Storage index buffer", BufferType.INDICES, BufferUsage.DYNAMIC_WRITE, byteBuffer);
|
|
} finally {
|
|
MemoryUtil.memFree(byteBuffer);
|
|
}
|
|
|
|
this.indexCount = neededIndexCount;
|
|
}
|
|
}
|
|
|
|
private it.unimi.dsi.fastutil.ints.IntConsumer intConsumer(ByteBuffer buffer) {
|
|
switch (this.type) {
|
|
case SHORT:
|
|
return i -> buffer.putShort((short)i);
|
|
case INT:
|
|
default:
|
|
return buffer::putInt;
|
|
}
|
|
}
|
|
|
|
public VertexFormat.IndexType type() {
|
|
return this.type;
|
|
}
|
|
}
|
|
|
|
@Environment(EnvType.CLIENT)
|
|
record GpuAsyncTask(Runnable callback, GpuFence fence) {
|
|
}
|
|
}
|