2025-11-24 22:52:51 +03:00

624 lines
32 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.ImmutableMap
* com.google.common.collect.ImmutableMap$Builder
* com.mojang.logging.LogUtils
* it.unimi.dsi.fastutil.objects.Object2IntMap
* it.unimi.dsi.fastutil.objects.Object2IntMap$Entry
* it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
* it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap
* org.apache.commons.lang3.mutable.MutableBoolean
* org.joml.Matrix3x2fc
* org.joml.Matrix4f
* org.joml.Matrix4fc
* org.joml.Vector3f
* org.joml.Vector3fc
* org.joml.Vector4f
* org.joml.Vector4fc
* org.jspecify.annotations.Nullable
* org.lwjgl.system.MemoryUtil
* org.slf4j.Logger
*/
package net.minecraft.client.gui.render;
import com.google.common.collect.ImmutableMap;
import com.mojang.blaze3d.ProjectionType;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.systems.CommandEncoder;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.FilterMode;
import com.mojang.blaze3d.textures.GpuTexture;
import com.mojang.blaze3d.textures.GpuTextureView;
import com.mojang.blaze3d.textures.TextureFormat;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Supplier;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.font.TextRenderable;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.render.TextureSetup;
import net.minecraft.client.gui.render.pip.OversizedItemRenderer;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import net.minecraft.client.gui.render.state.BlitRenderState;
import net.minecraft.client.gui.render.state.GlyphRenderState;
import net.minecraft.client.gui.render.state.GuiElementRenderState;
import net.minecraft.client.gui.render.state.GuiItemRenderState;
import net.minecraft.client.gui.render.state.GuiRenderState;
import net.minecraft.client.gui.render.state.pip.OversizedItemRenderState;
import net.minecraft.client.gui.render.state.pip.PictureInPictureRenderState;
import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer;
import net.minecraft.client.renderer.MappableRingBuffer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.feature.FeatureRenderDispatcher;
import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.util.Mth;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.joml.Matrix3x2fc;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import org.joml.Vector4f;
import org.joml.Vector4fc;
import org.jspecify.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
public class GuiRenderer
implements AutoCloseable {
private static final Logger LOGGER = LogUtils.getLogger();
private static final float MAX_GUI_Z = 10000.0f;
public static final float MIN_GUI_Z = 0.0f;
private static final float GUI_Z_NEAR = 1000.0f;
public static final int GUI_3D_Z_FAR = 1000;
public static final int GUI_3D_Z_NEAR = -1000;
public static final int DEFAULT_ITEM_SIZE = 16;
private static final int MINIMUM_ITEM_ATLAS_SIZE = 512;
private static final int MAXIMUM_ITEM_ATLAS_SIZE = RenderSystem.getDevice().getMaxTextureSize();
public static final int CLEAR_COLOR = 0;
private static final Comparator<ScreenRectangle> SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top).thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right));
private static final Comparator<TextureSetup> TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey));
private static final Comparator<GuiElementRenderState> ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR).thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)).thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR);
private final Map<Object, AtlasPosition> atlasPositions = new Object2ObjectOpenHashMap();
private final Map<Object, OversizedItemRenderer> oversizedItemRenderers = new Object2ObjectOpenHashMap();
private final GuiRenderState renderState;
private final List<Draw> draws = new ArrayList<Draw>();
private final List<MeshToDraw> meshesToDraw = new ArrayList<MeshToDraw>();
private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432);
private final Map<VertexFormat, MappableRingBuffer> vertexBuffers = new Object2ObjectOpenHashMap();
private int firstDrawIndexAfterBlur = Integer.MAX_VALUE;
private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0f, 11000.0f, true);
private final CachedOrthoProjectionMatrixBuffer itemsProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("items", -1000.0f, 1000.0f, true);
private final MultiBufferSource.BufferSource bufferSource;
private final SubmitNodeCollector submitNodeCollector;
private final FeatureRenderDispatcher featureRenderDispatcher;
private final Map<Class<? extends PictureInPictureRenderState>, PictureInPictureRenderer<?>> pictureInPictureRenderers;
private @Nullable GpuTexture itemsAtlas;
private @Nullable GpuTextureView itemsAtlasView;
private @Nullable GpuTexture itemsAtlasDepth;
private @Nullable GpuTextureView itemsAtlasDepthView;
private int itemAtlasX;
private int itemAtlasY;
private int cachedGuiScale;
private int frameNumber;
private @Nullable ScreenRectangle previousScissorArea = null;
private @Nullable RenderPipeline previousPipeline = null;
private @Nullable TextureSetup previousTextureSetup = null;
private @Nullable BufferBuilder bufferBuilder = null;
public GuiRenderer(GuiRenderState renderState, MultiBufferSource.BufferSource bufferSource, SubmitNodeCollector submitNodeCollector, FeatureRenderDispatcher featureRenderDispatcher, List<PictureInPictureRenderer<?>> pictureInPictureRenderers) {
this.renderState = renderState;
this.bufferSource = bufferSource;
this.submitNodeCollector = submitNodeCollector;
this.featureRenderDispatcher = featureRenderDispatcher;
ImmutableMap.Builder builder = ImmutableMap.builder();
for (PictureInPictureRenderer<?> pictureInPictureRenderer : pictureInPictureRenderers) {
builder.put(pictureInPictureRenderer.getRenderStateClass(), pictureInPictureRenderer);
}
this.pictureInPictureRenderers = builder.buildOrThrow();
}
public void incrementFrameNumber() {
++this.frameNumber;
}
public void render(GpuBufferSlice fogBuffer) {
this.prepare();
this.draw(fogBuffer);
for (MappableRingBuffer buffer : this.vertexBuffers.values()) {
buffer.rotate();
}
this.draws.clear();
this.meshesToDraw.clear();
this.renderState.reset();
this.firstDrawIndexAfterBlur = Integer.MAX_VALUE;
this.clearUnusedOversizedItemRenderers();
if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) {
RenderPipeline.updateSortKeySeed();
TextureSetup.updateSortKeySeed();
}
}
private void clearUnusedOversizedItemRenderers() {
Iterator<Map.Entry<Object, OversizedItemRenderer>> oversizedItemRendererIterator = this.oversizedItemRenderers.entrySet().iterator();
while (oversizedItemRendererIterator.hasNext()) {
Map.Entry<Object, OversizedItemRenderer> next = oversizedItemRendererIterator.next();
OversizedItemRenderer renderer = next.getValue();
if (!renderer.usedOnThisFrame()) {
renderer.close();
oversizedItemRendererIterator.remove();
continue;
}
renderer.resetUsedOnThisFrame();
}
}
private void prepare() {
this.bufferSource.endBatch();
this.preparePictureInPicture();
this.prepareItemElements();
this.prepareText();
this.renderState.sortElements(ELEMENT_SORT_COMPARATOR);
this.addElementsToMeshes(GuiRenderState.TraverseRange.BEFORE_BLUR);
this.firstDrawIndexAfterBlur = this.meshesToDraw.size();
this.addElementsToMeshes(GuiRenderState.TraverseRange.AFTER_BLUR);
this.recordDraws();
}
private void addElementsToMeshes(GuiRenderState.TraverseRange range) {
this.previousScissorArea = null;
this.previousPipeline = null;
this.previousTextureSetup = null;
this.bufferBuilder = null;
this.renderState.forEachElement(this::addElementToMesh, range);
if (this.bufferBuilder != null) {
this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea);
}
}
private void draw(GpuBufferSlice fogBuffer) {
if (this.draws.isEmpty()) {
return;
}
Minecraft minecraft = Minecraft.getInstance();
Window window = minecraft.getWindow();
RenderSystem.setProjectionMatrix(this.guiProjectionMatrixBuffer.getBuffer((float)window.getWidth() / (float)window.getGuiScale(), (float)window.getHeight() / (float)window.getGuiScale()), ProjectionType.ORTHOGRAPHIC);
RenderTarget mainRenderTarget = minecraft.getMainRenderTarget();
int maxIndexCount = 0;
for (Draw draw : this.draws) {
if (draw.indexCount <= maxIndexCount) continue;
maxIndexCount = draw.indexCount;
}
RenderSystem.AutoStorageIndexBuffer autoIndices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS);
GpuBuffer indexBuffer = autoIndices.getBuffer(maxIndexCount);
VertexFormat.IndexType indexType = autoIndices.type();
GpuBufferSlice dynamicTransforms = RenderSystem.getDynamicUniforms().writeTransform((Matrix4fc)new Matrix4f().setTranslation(0.0f, 0.0f, -11000.0f), (Vector4fc)new Vector4f(1.0f, 1.0f, 1.0f, 1.0f), (Vector3fc)new Vector3f(), (Matrix4fc)new Matrix4f());
if (this.firstDrawIndexAfterBlur > 0) {
this.executeDrawRange(() -> "GUI before blur", mainRenderTarget, fogBuffer, dynamicTransforms, indexBuffer, indexType, 0, Math.min(this.firstDrawIndexAfterBlur, this.draws.size()));
}
if (this.draws.size() <= this.firstDrawIndexAfterBlur) {
return;
}
RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(mainRenderTarget.getDepthTexture(), 1.0);
minecraft.gameRenderer.processBlurEffect();
this.executeDrawRange(() -> "GUI after blur", mainRenderTarget, fogBuffer, dynamicTransforms, indexBuffer, indexType, this.firstDrawIndexAfterBlur, this.draws.size());
}
private void executeDrawRange(Supplier<String> label, RenderTarget mainRenderTarget, GpuBufferSlice fogBuffer, GpuBufferSlice dynamicTransforms, GpuBuffer indexBuffer, VertexFormat.IndexType indexType, int startIndex, int endIndex) {
try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(label, mainRenderTarget.getColorTextureView(), OptionalInt.empty(), mainRenderTarget.useDepth ? mainRenderTarget.getDepthTextureView() : null, OptionalDouble.empty());){
RenderSystem.bindDefaultUniforms(renderPass);
renderPass.setUniform("Fog", fogBuffer);
renderPass.setUniform("DynamicTransforms", dynamicTransforms);
for (int i = startIndex; i < endIndex; ++i) {
Draw draw = this.draws.get(i);
this.executeDraw(draw, renderPass, indexBuffer, indexType);
}
}
}
private void addElementToMesh(GuiElementRenderState elementState) {
RenderPipeline pipeline = elementState.pipeline();
TextureSetup textureSetup = elementState.textureSetup();
ScreenRectangle scissorArea = elementState.scissorArea();
if (pipeline != this.previousPipeline || this.scissorChanged(scissorArea, this.previousScissorArea) || !textureSetup.equals(this.previousTextureSetup)) {
if (this.bufferBuilder != null) {
this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea);
}
this.bufferBuilder = this.getBufferBuilder(pipeline);
this.previousPipeline = pipeline;
this.previousTextureSetup = textureSetup;
this.previousScissorArea = scissorArea;
}
elementState.buildVertices(this.bufferBuilder);
}
private void prepareText() {
this.renderState.forEachText(text -> {
final Matrix3x2fc pose = text.pose;
final ScreenRectangle scissor = text.scissor;
text.ensurePrepared().visit(new Font.GlyphVisitor(){
@Override
public void acceptGlyph(TextRenderable.Styled glyph) {
this.accept(glyph);
}
@Override
public void acceptEffect(TextRenderable effect) {
this.accept(effect);
}
private void accept(TextRenderable glyph) {
GuiRenderer.this.renderState.submitGlyphToCurrentLayer(new GlyphRenderState(pose, glyph, scissor));
}
});
});
}
private void prepareItemElements() {
if (this.renderState.getItemModelIdentities().isEmpty()) {
return;
}
int guiScale = this.getGuiScaleInvalidatingItemAtlasIfChanged();
int singleItemTextureSize = 16 * guiScale;
int atlasSizeInPixels = this.calculateAtlasSizeInPixels(singleItemTextureSize);
if (this.itemsAtlas == null) {
this.createAtlasTextures(atlasSizeInPixels);
}
RenderSystem.outputColorTextureOverride = this.itemsAtlasView;
RenderSystem.outputDepthTextureOverride = this.itemsAtlasDepthView;
RenderSystem.setProjectionMatrix(this.itemsProjectionMatrixBuffer.getBuffer(atlasSizeInPixels, atlasSizeInPixels), ProjectionType.ORTHOGRAPHIC);
Minecraft.getInstance().gameRenderer.getLighting().setupFor(Lighting.Entry.ITEMS_3D);
PoseStack poseStack = new PoseStack();
MutableBoolean alreadyWarned = new MutableBoolean(false);
MutableBoolean hasOversizedItems = new MutableBoolean(false);
this.renderState.forEachItem(itemState -> {
int renderY;
boolean reDrawingAnimated;
if (itemState.oversizedItemBounds() != null) {
hasOversizedItems.setTrue();
return;
}
TrackingItemStackRenderState itemStackRenderState = itemState.itemStackRenderState();
AtlasPosition atlasPosition = this.atlasPositions.get(itemStackRenderState.getModelIdentity());
if (!(atlasPosition == null || itemStackRenderState.isAnimated() && atlasPosition.lastAnimatedOnFrame != this.frameNumber)) {
this.submitBlitFromItemAtlas((GuiItemRenderState)itemState, atlasPosition.u, atlasPosition.v, singleItemTextureSize, atlasSizeInPixels);
return;
}
if (this.itemAtlasX + singleItemTextureSize > atlasSizeInPixels) {
this.itemAtlasX = 0;
this.itemAtlasY += singleItemTextureSize;
}
boolean bl = reDrawingAnimated = itemStackRenderState.isAnimated() && atlasPosition != null;
if (!reDrawingAnimated && this.itemAtlasY + singleItemTextureSize > atlasSizeInPixels) {
if (alreadyWarned.isFalse()) {
LOGGER.warn("Trying to render too many items in GUI at the same time. Skipping some of them.");
alreadyWarned.setTrue();
}
return;
}
int renderX = reDrawingAnimated ? atlasPosition.x : this.itemAtlasX;
int n = renderY = reDrawingAnimated ? atlasPosition.y : this.itemAtlasY;
if (reDrawingAnimated) {
RenderSystem.getDevice().createCommandEncoder().clearColorAndDepthTextures(this.itemsAtlas, 0, this.itemsAtlasDepth, 1.0, renderX, atlasSizeInPixels - renderY - singleItemTextureSize, singleItemTextureSize, singleItemTextureSize);
}
this.renderItemToAtlas(itemStackRenderState, poseStack, renderX, renderY, singleItemTextureSize);
float u0 = (float)renderX / (float)atlasSizeInPixels;
float v0 = (float)(atlasSizeInPixels - renderY) / (float)atlasSizeInPixels;
this.submitBlitFromItemAtlas((GuiItemRenderState)itemState, u0, v0, singleItemTextureSize, atlasSizeInPixels);
if (reDrawingAnimated) {
atlasPosition.lastAnimatedOnFrame = this.frameNumber;
} else {
this.atlasPositions.put(itemState.itemStackRenderState().getModelIdentity(), new AtlasPosition(this.itemAtlasX, this.itemAtlasY, u0, v0, this.frameNumber));
this.itemAtlasX += singleItemTextureSize;
}
});
RenderSystem.outputColorTextureOverride = null;
RenderSystem.outputDepthTextureOverride = null;
if (hasOversizedItems.booleanValue()) {
this.renderState.forEachItem(itemState -> {
if (itemState.oversizedItemBounds() != null) {
TrackingItemStackRenderState itemStackRenderState = itemState.itemStackRenderState();
OversizedItemRenderer oversizedItemRenderer = this.oversizedItemRenderers.computeIfAbsent(itemStackRenderState.getModelIdentity(), key -> new OversizedItemRenderer(this.bufferSource));
ScreenRectangle actualItemBounds = itemState.oversizedItemBounds();
OversizedItemRenderState oversizedItemRenderState = new OversizedItemRenderState((GuiItemRenderState)itemState, actualItemBounds.left(), actualItemBounds.top(), actualItemBounds.right(), actualItemBounds.bottom());
oversizedItemRenderer.prepare(oversizedItemRenderState, this.renderState, guiScale);
}
});
}
}
private void preparePictureInPicture() {
int guiScale = Minecraft.getInstance().getWindow().getGuiScale();
this.renderState.forEachPictureInPicture(pictureInPictureState -> this.preparePictureInPictureState(pictureInPictureState, guiScale));
}
private <T extends PictureInPictureRenderState> void preparePictureInPictureState(T picturesInPictureState, int guiScale) {
PictureInPictureRenderer<?> renderer = this.pictureInPictureRenderers.get(picturesInPictureState.getClass());
if (renderer != null) {
renderer.prepare(picturesInPictureState, this.renderState, guiScale);
}
}
private void renderItemToAtlas(TrackingItemStackRenderState itemStackRenderState, PoseStack poseStack, int renderX, int renderY, int singleItemTextureSize) {
boolean flat;
poseStack.pushPose();
poseStack.translate((float)renderX + (float)singleItemTextureSize / 2.0f, (float)renderY + (float)singleItemTextureSize / 2.0f, 0.0f);
poseStack.scale(singleItemTextureSize, -singleItemTextureSize, singleItemTextureSize);
boolean bl = flat = !itemStackRenderState.usesBlockLight();
if (flat) {
Minecraft.getInstance().gameRenderer.getLighting().setupFor(Lighting.Entry.ITEMS_FLAT);
} else {
Minecraft.getInstance().gameRenderer.getLighting().setupFor(Lighting.Entry.ITEMS_3D);
}
RenderSystem.enableScissorForRenderTypeDraws(renderX, this.itemsAtlas.getHeight(0) - renderY - singleItemTextureSize, singleItemTextureSize, singleItemTextureSize);
itemStackRenderState.submit(poseStack, this.submitNodeCollector, 0xF000F0, OverlayTexture.NO_OVERLAY, 0);
this.featureRenderDispatcher.renderAllFeatures();
this.bufferSource.endBatch();
RenderSystem.disableScissorForRenderTypeDraws();
poseStack.popPose();
}
private void submitBlitFromItemAtlas(GuiItemRenderState itemState, float u0, float v0, int singleItemTextureSize, int atlasSizeInPixels) {
float u1 = u0 + (float)singleItemTextureSize / (float)atlasSizeInPixels;
float v1 = v0 + (float)(-singleItemTextureSize) / (float)atlasSizeInPixels;
this.renderState.submitBlitToCurrentLayer(new BlitRenderState(RenderPipelines.GUI_TEXTURED_PREMULTIPLIED_ALPHA, TextureSetup.singleTexture(this.itemsAtlasView, RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), itemState.pose(), itemState.x(), itemState.y(), itemState.x() + 16, itemState.y() + 16, u0, u1, v0, v1, -1, itemState.scissorArea(), null));
}
private void createAtlasTextures(int atlasSizeInPixels) {
GpuDevice device = RenderSystem.getDevice();
this.itemsAtlas = device.createTexture("UI items atlas", 12, TextureFormat.RGBA8, atlasSizeInPixels, atlasSizeInPixels, 1, 1);
this.itemsAtlasView = device.createTextureView(this.itemsAtlas);
this.itemsAtlasDepth = device.createTexture("UI items atlas depth", 8, TextureFormat.DEPTH32, atlasSizeInPixels, atlasSizeInPixels, 1, 1);
this.itemsAtlasDepthView = device.createTextureView(this.itemsAtlasDepth);
device.createCommandEncoder().clearColorAndDepthTextures(this.itemsAtlas, 0, this.itemsAtlasDepth, 1.0);
}
private int calculateAtlasSizeInPixels(int singleItemTextureSize) {
int itemCount;
Set<Object> itemStates = this.renderState.getItemModelIdentities();
if (this.atlasPositions.isEmpty()) {
itemCount = itemStates.size();
} else {
itemCount = this.atlasPositions.size();
for (Object itemState : itemStates) {
if (this.atlasPositions.containsKey(itemState)) continue;
++itemCount;
}
}
if (this.itemsAtlas != null) {
int currentAtlasItemsPerRow = this.itemsAtlas.getWidth(0) / singleItemTextureSize;
int currentAtlasCapacity = currentAtlasItemsPerRow * currentAtlasItemsPerRow;
if (itemCount < currentAtlasCapacity) {
return this.itemsAtlas.getWidth(0);
}
this.invalidateItemAtlas();
}
int itemCountOnThisFrame = itemStates.size();
int atlasSizeInItems = Mth.smallestSquareSide(itemCountOnThisFrame + itemCountOnThisFrame / 2);
return Math.clamp((long)Mth.smallestEncompassingPowerOfTwo(atlasSizeInItems * singleItemTextureSize), 512, MAXIMUM_ITEM_ATLAS_SIZE);
}
private int getGuiScaleInvalidatingItemAtlasIfChanged() {
int guiScale = Minecraft.getInstance().getWindow().getGuiScale();
if (guiScale != this.cachedGuiScale) {
this.invalidateItemAtlas();
for (OversizedItemRenderer renderer : this.oversizedItemRenderers.values()) {
renderer.invalidateTexture();
}
this.cachedGuiScale = guiScale;
}
return guiScale;
}
private void invalidateItemAtlas() {
this.itemAtlasX = 0;
this.itemAtlasY = 0;
this.atlasPositions.clear();
if (this.itemsAtlas != null) {
this.itemsAtlas.close();
this.itemsAtlas = null;
}
if (this.itemsAtlasView != null) {
this.itemsAtlasView.close();
this.itemsAtlasView = null;
}
if (this.itemsAtlasDepth != null) {
this.itemsAtlasDepth.close();
this.itemsAtlasDepth = null;
}
if (this.itemsAtlasDepthView != null) {
this.itemsAtlasDepthView.close();
this.itemsAtlasDepthView = null;
}
}
private void recordMesh(BufferBuilder bufferBuilder, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) {
MeshData mesh = bufferBuilder.build();
if (mesh != null) {
this.meshesToDraw.add(new MeshToDraw(mesh, pipeline, textureSetup, scissorArea));
}
}
private void recordDraws() {
this.ensureVertexBufferSizes();
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
Object2IntOpenHashMap offsets = new Object2IntOpenHashMap();
for (MeshToDraw meshToDraw : this.meshesToDraw) {
MeshData mesh = meshToDraw.mesh;
MeshData.DrawState drawState = mesh.drawState();
VertexFormat format = drawState.format();
MappableRingBuffer vertexBuffer = this.vertexBuffers.get(format);
if (!offsets.containsKey((Object)format)) {
offsets.put((Object)format, 0);
}
ByteBuffer meshVertexBuffer = mesh.vertexBuffer();
int meshBufferSize = meshVertexBuffer.remaining();
int offset = offsets.getInt((Object)format);
try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(offset, meshBufferSize), false, true);){
MemoryUtil.memCopy((ByteBuffer)meshVertexBuffer, (ByteBuffer)mappedView.data());
}
offsets.put((Object)format, offset + meshBufferSize);
this.draws.add(new Draw(vertexBuffer.currentBuffer(), offset / format.getVertexSize(), drawState.mode(), drawState.indexCount(), meshToDraw.pipeline, meshToDraw.textureSetup, meshToDraw.scissorArea));
meshToDraw.close();
}
}
private void ensureVertexBufferSizes() {
Object2IntMap<VertexFormat> requiredSizes = this.calculatedRequiredVertexBufferSizes();
for (Object2IntMap.Entry entry : requiredSizes.object2IntEntrySet()) {
VertexFormat vertexFormat = (VertexFormat)entry.getKey();
int requiredSize = entry.getIntValue();
MappableRingBuffer vertexBuffer = this.vertexBuffers.get(vertexFormat);
if (vertexBuffer != null && vertexBuffer.size() >= requiredSize) continue;
if (vertexBuffer != null) {
vertexBuffer.close();
}
this.vertexBuffers.put(vertexFormat, new MappableRingBuffer(() -> "GUI vertex buffer for " + String.valueOf(vertexFormat), 34, requiredSize));
}
}
private Object2IntMap<VertexFormat> calculatedRequiredVertexBufferSizes() {
Object2IntOpenHashMap requiredVertexBufferSizes = new Object2IntOpenHashMap();
for (MeshToDraw meshToDraw : this.meshesToDraw) {
MeshData.DrawState drawState = meshToDraw.mesh.drawState();
VertexFormat format = drawState.format();
if (!requiredVertexBufferSizes.containsKey((Object)format)) {
requiredVertexBufferSizes.put((Object)format, 0);
}
requiredVertexBufferSizes.put((Object)format, requiredVertexBufferSizes.getInt((Object)format) + drawState.vertexCount() * format.getVertexSize());
}
return requiredVertexBufferSizes;
}
private void executeDraw(Draw draw, RenderPass renderPass, GpuBuffer indexBuffer, VertexFormat.IndexType indexType) {
RenderPipeline pipeline = draw.pipeline();
renderPass.setPipeline(pipeline);
renderPass.setVertexBuffer(0, draw.vertexBuffer);
ScreenRectangle scissorArea = draw.scissorArea();
if (scissorArea != null) {
this.enableScissor(scissorArea, renderPass);
} else {
renderPass.disableScissor();
}
if (draw.textureSetup.texure0() != null) {
renderPass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0());
}
if (draw.textureSetup.texure1() != null) {
renderPass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1());
}
if (draw.textureSetup.texure2() != null) {
renderPass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2());
}
renderPass.setIndexBuffer(indexBuffer, indexType);
renderPass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1);
}
private BufferBuilder getBufferBuilder(RenderPipeline pipeline) {
return new BufferBuilder(this.byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat());
}
private boolean scissorChanged(@Nullable ScreenRectangle newScissor, @Nullable ScreenRectangle oldScissor) {
if (newScissor == oldScissor) {
return false;
}
if (newScissor != null) {
return !newScissor.equals(oldScissor);
}
return true;
}
private void enableScissor(ScreenRectangle rectangle, RenderPass renderPass) {
Window window = Minecraft.getInstance().getWindow();
int windowHeight = window.getHeight();
int guiScale = window.getGuiScale();
double left = rectangle.left() * guiScale;
double bottom = windowHeight - rectangle.bottom() * guiScale;
double width = rectangle.width() * guiScale;
double height = rectangle.height() * guiScale;
renderPass.enableScissor((int)left, (int)bottom, Math.max(0, (int)width), Math.max(0, (int)height));
}
@Override
public void close() {
this.byteBufferBuilder.close();
if (this.itemsAtlas != null) {
this.itemsAtlas.close();
}
if (this.itemsAtlasView != null) {
this.itemsAtlasView.close();
}
if (this.itemsAtlasDepth != null) {
this.itemsAtlasDepth.close();
}
if (this.itemsAtlasDepthView != null) {
this.itemsAtlasDepthView.close();
}
this.pictureInPictureRenderers.values().forEach(PictureInPictureRenderer::close);
this.guiProjectionMatrixBuffer.close();
this.itemsProjectionMatrixBuffer.close();
for (MappableRingBuffer buffer : this.vertexBuffers.values()) {
buffer.close();
}
this.oversizedItemRenderers.values().forEach(PictureInPictureRenderer::close);
}
private record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) {
}
private record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) implements AutoCloseable
{
@Override
public void close() {
this.mesh.close();
}
}
private static final class AtlasPosition {
final int x;
final int y;
final float u;
final float v;
int lastAnimatedOnFrame;
private AtlasPosition(int x, int y, float u, float v, int lastAnimatedOnFrame) {
this.x = x;
this.y = y;
this.u = u;
this.v = v;
this.lastAnimatedOnFrame = lastAnimatedOnFrame;
}
}
}