/* * 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 SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top).thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right)); private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR).thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)).thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); private final Map atlasPositions = new Object2ObjectOpenHashMap(); private final Map oversizedItemRenderers = new Object2ObjectOpenHashMap(); private final GuiRenderState renderState; private final List draws = new ArrayList(); private final List meshesToDraw = new ArrayList(); private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); private final Map 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, 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> 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> oversizedItemRendererIterator = this.oversizedItemRenderers.entrySet().iterator(); while (oversizedItemRendererIterator.hasNext()) { Map.Entry 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 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 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 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 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 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; } } }