minecraft-src/com/mojang/blaze3d/systems/RenderSystem.java
2025-07-04 03:45:38 +03:00

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) {
}
}