minecraft-src/net/minecraft/client/gui/render/GuiRenderer.java
2025-09-18 12:46:36 +00:00

720 lines
27 KiB
Java

package net.minecraft.client.gui.render;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.mojang.blaze3d.ProjectionType;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.buffers.GpuBuffer.MappedView;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.pipeline.RenderTarget;
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.systems.RenderSystem.AutoStorageIndexBuffer;
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.blaze3d.vertex.MeshData.DrawState;
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.Map.Entry;
import java.util.function.Supplier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font.GlyphVisitor;
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
import net.minecraft.client.gui.font.glyphs.BakedGlyph.Effect;
import net.minecraft.client.gui.font.glyphs.BakedGlyph.GlyphInstance;
import net.minecraft.client.gui.navigation.ScreenRectangle;
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.GlyphEffectRenderState;
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.GuiRenderState.TraverseRange;
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.RenderPipelines;
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
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.jetbrains.annotations.Nullable;
import org.joml.Matrix3x2f;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class GuiRenderer implements AutoCloseable {
private static final Logger LOGGER = LogUtils.getLogger();
private static final float MAX_GUI_Z = 10000.0F;
private 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, GuiRenderer.AtlasPosition> atlasPositions = new Object2ObjectOpenHashMap<>();
private final Map<Object, OversizedItemRenderer> oversizedItemRenderers = new Object2ObjectOpenHashMap<>();
final GuiRenderState renderState;
private final List<GuiRenderer.Draw> draws = new ArrayList();
private final List<GuiRenderer.MeshToDraw> meshesToDraw = new ArrayList();
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 BufferSource bufferSource;
private final Map<Class<? extends PictureInPictureRenderState>, PictureInPictureRenderer<?>> pictureInPictureRenderers;
@Nullable
private GpuTexture itemsAtlas;
@Nullable
private GpuTextureView itemsAtlasView;
@Nullable
private GpuTexture itemsAtlasDepth;
@Nullable
private GpuTextureView itemsAtlasDepthView;
private int itemAtlasX;
private int itemAtlasY;
private int cachedGuiScale;
private int frameNumber;
@Nullable
private ScreenRectangle previousScissorArea = null;
@Nullable
private RenderPipeline previousPipeline = null;
@Nullable
private TextureSetup previousTextureSetup = null;
@Nullable
private BufferBuilder bufferBuilder = null;
public GuiRenderer(GuiRenderState renderState, BufferSource bufferSource, List<PictureInPictureRenderer<?>> pictureInPictureRenderers) {
this.renderState = renderState;
this.bufferSource = bufferSource;
Builder<Class<? extends PictureInPictureRenderState>, PictureInPictureRenderer<?>> builder = ImmutableMap.builder();
for (PictureInPictureRenderer<?> pictureInPictureRenderer : pictureInPictureRenderers) {
builder.put((Class<? extends PictureInPictureRenderState>)pictureInPictureRenderer.getRenderStateClass(), pictureInPictureRenderer);
}
this.pictureInPictureRenderers = builder.buildOrThrow();
}
public void incrementFrameNumber() {
this.frameNumber++;
}
public void render(GpuBufferSlice bufferSlice) {
this.prepare();
this.draw(bufferSlice);
for (MappableRingBuffer mappableRingBuffer : this.vertexBuffers.values()) {
mappableRingBuffer.rotate();
}
this.draws.clear();
this.meshesToDraw.clear();
this.renderState.reset();
this.firstDrawIndexAfterBlur = Integer.MAX_VALUE;
this.clearUnusedOversizedItemRenderers();
}
private void clearUnusedOversizedItemRenderers() {
Iterator<Entry<Object, OversizedItemRenderer>> iterator = this.oversizedItemRenderers.entrySet().iterator();
while (iterator.hasNext()) {
Entry<Object, OversizedItemRenderer> entry = (Entry<Object, OversizedItemRenderer>)iterator.next();
OversizedItemRenderer oversizedItemRenderer = (OversizedItemRenderer)entry.getValue();
if (!oversizedItemRenderer.usedOnThisFrame()) {
oversizedItemRenderer.close();
iterator.remove();
} else {
oversizedItemRenderer.resetUsedOnThisFrame();
}
}
}
private void prepare() {
this.bufferSource.endBatch();
this.preparePictureInPicture();
this.prepareItemElements();
this.prepareText();
this.renderState.sortElements(ELEMENT_SORT_COMPARATOR);
this.addElementsToMeshes(TraverseRange.BEFORE_BLUR);
this.firstDrawIndexAfterBlur = this.meshesToDraw.size();
this.addElementsToMeshes(TraverseRange.AFTER_BLUR);
this.recordDraws();
}
private void addElementsToMeshes(TraverseRange traverseRange) {
this.previousScissorArea = null;
this.previousPipeline = null;
this.previousTextureSetup = null;
this.bufferBuilder = null;
this.renderState.forEachElement(this::addElementToMesh, traverseRange);
if (this.bufferBuilder != null) {
this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea);
}
}
private void draw(GpuBufferSlice fogUniforms) {
if (!this.draws.isEmpty()) {
Minecraft minecraft = Minecraft.getInstance();
Window window = minecraft.getWindow();
RenderSystem.setProjectionMatrix(
this.guiProjectionMatrixBuffer.getBuffer((float)window.getWidth() / window.getGuiScale(), (float)window.getHeight() / window.getGuiScale()),
ProjectionType.ORTHOGRAPHIC
);
RenderTarget renderTarget = minecraft.getMainRenderTarget();
int i = 0;
for (GuiRenderer.Draw draw : this.draws) {
if (draw.indexCount > i) {
i = draw.indexCount;
}
}
AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS);
GpuBuffer gpuBuffer = autoStorageIndexBuffer.getBuffer(i);
VertexFormat.IndexType indexType = autoStorageIndexBuffer.type();
GpuBufferSlice gpuBufferSlice = RenderSystem.getDynamicUniforms()
.writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f(), 0.0F);
if (this.firstDrawIndexAfterBlur > 0) {
this.executeDrawRange(
() -> "GUI before blur", renderTarget, fogUniforms, gpuBufferSlice, gpuBuffer, indexType, 0, Math.min(this.firstDrawIndexAfterBlur, this.draws.size())
);
}
if (this.draws.size() > this.firstDrawIndexAfterBlur) {
RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(renderTarget.getDepthTexture(), 1.0);
minecraft.gameRenderer.processBlurEffect();
this.executeDrawRange(
() -> "GUI after blur", renderTarget, fogUniforms, gpuBufferSlice, gpuBuffer, indexType, this.firstDrawIndexAfterBlur, this.draws.size()
);
}
}
}
private void executeDrawRange(
Supplier<String> debugGroup,
RenderTarget renderTarget,
GpuBufferSlice fogUniforms,
GpuBufferSlice dynamicTransforms,
GpuBuffer buffer,
VertexFormat.IndexType indexType,
int start,
int end
) {
try (RenderPass renderPass = RenderSystem.getDevice()
.createCommandEncoder()
.createRenderPass(
debugGroup,
renderTarget.getColorTextureView(),
OptionalInt.empty(),
renderTarget.useDepth ? renderTarget.getDepthTextureView() : null,
OptionalDouble.empty()
)) {
RenderSystem.bindDefaultUniforms(renderPass);
renderPass.setUniform("Fog", fogUniforms);
renderPass.setUniform("DynamicTransforms", dynamicTransforms);
for (int i = start; i < end; i++) {
GuiRenderer.Draw draw = (GuiRenderer.Draw)this.draws.get(i);
this.executeDraw(draw, renderPass, buffer, indexType);
}
}
}
private void addElementToMesh(GuiElementRenderState renderState, int layer) {
RenderPipeline renderPipeline = renderState.pipeline();
TextureSetup textureSetup = renderState.textureSetup();
ScreenRectangle screenRectangle = renderState.scissorArea();
if (renderPipeline != this.previousPipeline
|| this.scissorChanged(screenRectangle, this.previousScissorArea)
|| !textureSetup.equals(this.previousTextureSetup)) {
if (this.bufferBuilder != null) {
this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea);
}
this.bufferBuilder = this.getBufferBuilder(renderPipeline);
this.previousPipeline = renderPipeline;
this.previousTextureSetup = textureSetup;
this.previousScissorArea = screenRectangle;
}
renderState.buildVertices(this.bufferBuilder, 0.0F + layer);
}
private void prepareText() {
this.renderState.forEachText(guiTextRenderState -> {
final Matrix3x2f matrix3x2f = guiTextRenderState.pose;
final ScreenRectangle screenRectangle = guiTextRenderState.scissor;
guiTextRenderState.ensurePrepared().visit(new GlyphVisitor() {
@Override
public void acceptGlyph(GlyphInstance glyph) {
if (glyph.glyph().textureView() != null) {
GuiRenderer.this.renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2f, glyph, screenRectangle));
}
}
@Override
public void acceptEffect(BakedGlyph glyph, Effect effect) {
if (glyph.textureView() != null) {
GuiRenderer.this.renderState.submitGlyphToCurrentLayer(new GlyphEffectRenderState(matrix3x2f, glyph, effect, screenRectangle));
}
}
});
});
}
private void prepareItemElements() {
if (!this.renderState.getItemModelIdentities().isEmpty()) {
int i = this.getGuiScaleInvalidatingItemAtlasIfChanged();
int j = 16 * i;
int k = this.calculateAtlasSizeInPixels(j);
if (this.itemsAtlas == null) {
this.createAtlasTextures(k);
}
RenderSystem.outputColorTextureOverride = this.itemsAtlasView;
RenderSystem.outputDepthTextureOverride = this.itemsAtlasDepthView;
RenderSystem.setProjectionMatrix(this.itemsProjectionMatrixBuffer.getBuffer(k, k), ProjectionType.ORTHOGRAPHIC);
Minecraft.getInstance().gameRenderer.getLighting().setupFor(com.mojang.blaze3d.platform.Lighting.Entry.ITEMS_3D);
PoseStack poseStack = new PoseStack();
MutableBoolean mutableBoolean = new MutableBoolean(false);
MutableBoolean mutableBoolean2 = new MutableBoolean(false);
this.renderState
.forEachItem(
guiItemRenderState -> {
if (guiItemRenderState.oversizedItemBounds() != null) {
mutableBoolean2.setTrue();
} else {
TrackingItemStackRenderState trackingItemStackRenderState = guiItemRenderState.itemStackRenderState();
GuiRenderer.AtlasPosition atlasPosition = (GuiRenderer.AtlasPosition)this.atlasPositions.get(trackingItemStackRenderState.getModelIdentity());
if (atlasPosition == null || trackingItemStackRenderState.isAnimated() && atlasPosition.lastAnimatedOnFrame != this.frameNumber) {
if (this.itemAtlasX + j > k) {
this.itemAtlasX = 0;
this.itemAtlasY += j;
}
boolean bl = trackingItemStackRenderState.isAnimated() && atlasPosition != null;
if (!bl && this.itemAtlasY + j > k) {
if (mutableBoolean.isFalse()) {
LOGGER.warn("Trying to render too many items in GUI at the same time. Skipping some of them.");
mutableBoolean.setTrue();
}
} else {
int kx = bl ? atlasPosition.x : this.itemAtlasX;
int l = bl ? atlasPosition.y : this.itemAtlasY;
if (bl) {
RenderSystem.getDevice().createCommandEncoder().clearColorAndDepthTextures(this.itemsAtlas, 0, this.itemsAtlasDepth, 1.0, kx, k - l - j, j, j);
}
this.renderItemToAtlas(trackingItemStackRenderState, poseStack, kx, l, j);
float f = (float)kx / k;
float g = (float)(k - l) / k;
this.submitBlitFromItemAtlas(guiItemRenderState, f, g, j, k);
if (bl) {
atlasPosition.lastAnimatedOnFrame = this.frameNumber;
} else {
this.atlasPositions
.put(
guiItemRenderState.itemStackRenderState().getModelIdentity(),
new GuiRenderer.AtlasPosition(this.itemAtlasX, this.itemAtlasY, f, g, this.frameNumber)
);
this.itemAtlasX += j;
}
}
} else {
this.submitBlitFromItemAtlas(guiItemRenderState, atlasPosition.u, atlasPosition.v, j, k);
}
}
}
);
RenderSystem.outputColorTextureOverride = null;
RenderSystem.outputDepthTextureOverride = null;
if (mutableBoolean2.getValue()) {
this.renderState
.forEachItem(
guiItemRenderState -> {
if (guiItemRenderState.oversizedItemBounds() != null) {
TrackingItemStackRenderState trackingItemStackRenderState = guiItemRenderState.itemStackRenderState();
OversizedItemRenderer oversizedItemRenderer = (OversizedItemRenderer)this.oversizedItemRenderers
.computeIfAbsent(trackingItemStackRenderState.getModelIdentity(), object -> new OversizedItemRenderer(this.bufferSource));
ScreenRectangle screenRectangle = guiItemRenderState.oversizedItemBounds();
OversizedItemRenderState oversizedItemRenderState = new OversizedItemRenderState(
guiItemRenderState, screenRectangle.left(), screenRectangle.top(), screenRectangle.right(), screenRectangle.bottom()
);
oversizedItemRenderer.prepare(oversizedItemRenderState, this.renderState, i);
}
}
);
}
}
}
private void preparePictureInPicture() {
int i = Minecraft.getInstance().getWindow().getGuiScale();
this.renderState.forEachPictureInPicture(pictureInPictureRenderState -> this.preparePictureInPictureState(pictureInPictureRenderState, i));
}
private <T extends PictureInPictureRenderState> void preparePictureInPictureState(T state, int guiScale) {
PictureInPictureRenderer<T> pictureInPictureRenderer = (PictureInPictureRenderer<T>)this.pictureInPictureRenderers.get(state.getClass());
if (pictureInPictureRenderer != null) {
pictureInPictureRenderer.prepare(state, this.renderState, guiScale);
}
}
private void renderItemToAtlas(TrackingItemStackRenderState renderState, PoseStack poseStack, int x, int y, int size) {
poseStack.pushPose();
poseStack.translate(x + size / 2.0F, y + size / 2.0F, 0.0F);
poseStack.scale(size, -size, size);
boolean bl = !renderState.usesBlockLight();
if (bl) {
Minecraft.getInstance().gameRenderer.getLighting().setupFor(com.mojang.blaze3d.platform.Lighting.Entry.ITEMS_FLAT);
} else {
Minecraft.getInstance().gameRenderer.getLighting().setupFor(com.mojang.blaze3d.platform.Lighting.Entry.ITEMS_3D);
}
RenderSystem.enableScissorForRenderTypeDraws(x, this.itemsAtlas.getHeight(0) - y - size, size, size);
renderState.render(poseStack, this.bufferSource, 15728880, OverlayTexture.NO_OVERLAY);
this.bufferSource.endBatch();
RenderSystem.disableScissorForRenderTypeDraws();
poseStack.popPose();
}
private void submitBlitFromItemAtlas(GuiItemRenderState renderState, float x, float y, int itemSize, int atlasSize) {
float f = x + (float)itemSize / atlasSize;
float g = y + (float)(-itemSize) / atlasSize;
this.renderState
.submitBlitToCurrentLayer(
new BlitRenderState(
RenderPipelines.GUI_TEXTURED_PREMULTIPLIED_ALPHA,
TextureSetup.singleTexture(this.itemsAtlasView),
renderState.pose(),
renderState.x(),
renderState.y(),
renderState.x() + 16,
renderState.y() + 16,
x,
f,
y,
g,
-1,
renderState.scissorArea(),
null
)
);
}
private void createAtlasTextures(int atlasSize) {
GpuDevice gpuDevice = RenderSystem.getDevice();
this.itemsAtlas = gpuDevice.createTexture("UI items atlas", 12, TextureFormat.RGBA8, atlasSize, atlasSize, 1, 1);
this.itemsAtlas.setTextureFilter(FilterMode.NEAREST, false);
this.itemsAtlasView = gpuDevice.createTextureView(this.itemsAtlas);
this.itemsAtlasDepth = gpuDevice.createTexture("UI items atlas depth", 8, TextureFormat.DEPTH32, atlasSize, atlasSize, 1, 1);
this.itemsAtlasDepthView = gpuDevice.createTextureView(this.itemsAtlasDepth);
gpuDevice.createCommandEncoder().clearColorAndDepthTextures(this.itemsAtlas, 0, this.itemsAtlasDepth, 1.0);
}
private int calculateAtlasSizeInPixels(int itemWidth) {
Set<Object> set = this.renderState.getItemModelIdentities();
int i;
if (this.atlasPositions.isEmpty()) {
i = set.size();
} else {
i = this.atlasPositions.size();
for (Object object : set) {
if (!this.atlasPositions.containsKey(object)) {
i++;
}
}
}
if (this.itemsAtlas != null) {
int j = this.itemsAtlas.getWidth(0) / itemWidth;
int k = j * j;
if (i < k) {
return this.itemsAtlas.getWidth(0);
}
this.invalidateItemAtlas();
}
int j = set.size();
int k = Mth.smallestSquareSide(j + j / 2);
return Math.clamp(Mth.smallestEncompassingPowerOfTwo(k * itemWidth), 512, MAXIMUM_ITEM_ATLAS_SIZE);
}
private int getGuiScaleInvalidatingItemAtlasIfChanged() {
int i = Minecraft.getInstance().getWindow().getGuiScale();
if (i != this.cachedGuiScale) {
this.invalidateItemAtlas();
for (OversizedItemRenderer oversizedItemRenderer : this.oversizedItemRenderers.values()) {
oversizedItemRenderer.invalidateTexture();
}
this.cachedGuiScale = i;
}
return i;
}
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 meshData = bufferBuilder.buildOrThrow();
this.meshesToDraw.add(new GuiRenderer.MeshToDraw(meshData, pipeline, textureSetup, scissorArea));
}
private void recordDraws() {
this.ensureVertexBufferSizes();
CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder();
Object2IntMap<VertexFormat> object2IntMap = new Object2IntOpenHashMap<>();
for (GuiRenderer.MeshToDraw meshToDraw : this.meshesToDraw) {
MeshData meshData = meshToDraw.mesh;
DrawState drawState = meshData.drawState();
VertexFormat vertexFormat = drawState.format();
MappableRingBuffer mappableRingBuffer = (MappableRingBuffer)this.vertexBuffers.get(vertexFormat);
if (!object2IntMap.containsKey(vertexFormat)) {
object2IntMap.put(vertexFormat, 0);
}
ByteBuffer byteBuffer = meshData.vertexBuffer();
int i = byteBuffer.remaining();
int j = object2IntMap.getInt(vertexFormat);
try (MappedView mappedView = commandEncoder.mapBuffer(mappableRingBuffer.currentBuffer().slice(j, i), false, true)) {
MemoryUtil.memCopy(byteBuffer, mappedView.data());
}
object2IntMap.put(vertexFormat, j + i);
this.draws
.add(
new GuiRenderer.Draw(
mappableRingBuffer.currentBuffer(),
j / vertexFormat.getVertexSize(),
drawState.mode(),
drawState.indexCount(),
meshToDraw.pipeline,
meshToDraw.textureSetup,
meshToDraw.scissorArea
)
);
meshToDraw.close();
}
}
private void ensureVertexBufferSizes() {
Object2IntMap<VertexFormat> object2IntMap = this.calculatedRequiredVertexBufferSizes();
for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<VertexFormat> entry : object2IntMap.object2IntEntrySet()) {
VertexFormat vertexFormat = (VertexFormat)entry.getKey();
int i = entry.getIntValue();
MappableRingBuffer mappableRingBuffer = (MappableRingBuffer)this.vertexBuffers.get(vertexFormat);
if (mappableRingBuffer == null || mappableRingBuffer.size() < i) {
if (mappableRingBuffer != null) {
mappableRingBuffer.close();
}
this.vertexBuffers.put(vertexFormat, new MappableRingBuffer(() -> "GUI vertex buffer for " + vertexFormat, 34, i));
}
}
}
private Object2IntMap<VertexFormat> calculatedRequiredVertexBufferSizes() {
Object2IntMap<VertexFormat> object2IntMap = new Object2IntOpenHashMap<>();
for (GuiRenderer.MeshToDraw meshToDraw : this.meshesToDraw) {
DrawState drawState = meshToDraw.mesh.drawState();
VertexFormat vertexFormat = drawState.format();
if (!object2IntMap.containsKey(vertexFormat)) {
object2IntMap.put(vertexFormat, 0);
}
object2IntMap.put(vertexFormat, object2IntMap.getInt(vertexFormat) + drawState.vertexCount() * vertexFormat.getVertexSize());
}
return object2IntMap;
}
private void executeDraw(GuiRenderer.Draw draw, RenderPass renderPass, GpuBuffer buffer, VertexFormat.IndexType indexType) {
RenderPipeline renderPipeline = draw.pipeline();
renderPass.setPipeline(renderPipeline);
renderPass.setVertexBuffer(0, draw.vertexBuffer);
ScreenRectangle screenRectangle = draw.scissorArea();
if (screenRectangle != null) {
this.enableScissor(screenRectangle, renderPass);
} else {
renderPass.disableScissor();
}
if (draw.textureSetup.texure0() != null) {
renderPass.bindSampler("Sampler0", draw.textureSetup.texure0());
}
if (draw.textureSetup.texure1() != null) {
renderPass.bindSampler("Sampler1", draw.textureSetup.texure1());
}
if (draw.textureSetup.texure2() != null) {
renderPass.bindSampler("Sampler2", draw.textureSetup.texure2());
}
renderPass.setIndexBuffer(buffer, 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 scissorArea, @Nullable ScreenRectangle oldScissorArea) {
if (scissorArea == oldScissorArea) {
return false;
} else {
return scissorArea != null ? !scissorArea.equals(oldScissorArea) : true;
}
}
private void enableScissor(ScreenRectangle scissorArea, RenderPass renderPass) {
Window window = Minecraft.getInstance().getWindow();
int i = window.getHeight();
int j = window.getGuiScale();
double d = scissorArea.left() * j;
double e = i - scissorArea.bottom() * j;
double f = scissorArea.width() * j;
double g = scissorArea.height() * j;
renderPass.enableScissor((int)d, (int)e, Math.max(0, (int)f), Math.max(0, (int)g));
}
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 mappableRingBuffer : this.vertexBuffers.values()) {
mappableRingBuffer.close();
}
this.oversizedItemRenderers.values().forEach(PictureInPictureRenderer::close);
}
@Environment(EnvType.CLIENT)
static final class AtlasPosition {
final int x;
final int y;
final float u;
final float v;
int lastAnimatedOnFrame;
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;
}
}
@Environment(EnvType.CLIENT)
record Draw(
GpuBuffer vertexBuffer,
int baseVertex,
VertexFormat.Mode mode,
int indexCount,
RenderPipeline pipeline,
TextureSetup textureSetup,
@Nullable ScreenRectangle scissorArea
) {
}
@Environment(EnvType.CLIENT)
record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) implements AutoCloseable {
public void close() {
this.mesh.close();
}
}
}