package net.minecraft.client.renderer;

import com.mojang.blaze3d.GpuFormat;
import com.mojang.blaze3d.IndexType;
import com.mojang.blaze3d.buffers.GpuBuffer;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.framegraph.FramePass;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.pipeline.TextureTarget;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import com.mojang.blaze3d.resource.RenderTargetDescriptor;
import com.mojang.blaze3d.resource.ResourceHandle;
import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.AddressMode;
import com.mojang.blaze3d.textures.FilterMode;
import com.mojang.blaze3d.textures.GpuSampler;
import com.mojang.blaze3d.textures.GpuTextureView;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongCollection;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.OptionalDouble;
import net.minecraft.SharedConstants;
import net.minecraft.client.Camera;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.client.PrioritizeChunkUpdates;
import net.minecraft.client.TextureFilteringMethod;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.block.dispatch.BlockStateModel;
import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState;
import net.minecraft.client.renderer.chunk.ChunkSectionLayer;
import net.minecraft.client.renderer.chunk.ChunkSectionLayerGroup;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.chunk.CompiledSectionMesh;
import net.minecraft.client.renderer.chunk.SectionCompiler;
import net.minecraft.client.renderer.chunk.SectionMesh;
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
import net.minecraft.client.renderer.chunk.TranslucencyPointOfView;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.feature.FeatureRenderDispatcher;
import net.minecraft.client.renderer.gizmos.DrawableGizmoPrimitives;
import net.minecraft.client.renderer.rendertype.RenderType;
import net.minecraft.client.renderer.rendertype.RenderTypes;
import net.minecraft.client.renderer.state.OptionsRenderState;
import net.minecraft.client.renderer.state.level.BlockBreakingRenderState;
import net.minecraft.client.renderer.state.level.BlockOutlineRenderState;
import net.minecraft.client.renderer.state.level.CameraRenderState;
import net.minecraft.client.renderer.state.level.LevelRenderState;
import net.minecraft.client.renderer.state.level.SectionUpdateRenderState;
import net.minecraft.client.renderer.state.level.SkyRenderState;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.sprite.AtlasManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.gizmos.Gizmos;
import net.minecraft.gizmos.SimpleGizmoCollector;
import net.minecraft.resources.Identifier;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.Util;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.LeavesBlock;
import net.lax1dude.eaglercraft.v1_8.mesh.MeshWorkerRuntime;
import net.lax1dude.eaglercraft.v1_8.mesh.ModelTableCodec;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.material.FogType;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.joml.Matrix4fc;
import org.joml.Vector4f;
import org.joml.Vector4fc;
import org.jspecify.annotations.Nullable;

public class LevelRenderer implements AutoCloseable {
   private static final Identifier TRANSPARENCY_POST_CHAIN_ID = Identifier.withDefaultNamespace("transparency");
   private static final Identifier ENTITY_OUTLINE_POST_CHAIN_ID = Identifier.withDefaultNamespace("entity_outline");
   private static final int MINIMUM_TRANSPARENT_SORT_COUNT = 15;
   private static final float CHUNK_VISIBILITY_THRESHOLD = 0.3F;
   private static final Vector4fc SCREEN_SIZE_TARGET_CLEAR_COLOR = new Vector4f(0.0F);
   private static final Vector4fc ENTITY_OUTLINE_CLEAR_COLOR = new Vector4f(0.0F);
   private final GameRenderer gameRenderer;
   private final EntityRenderDispatcher entityRenderDispatcher;
   private final BlockEntityRenderDispatcher blockEntityRenderDispatcher;
   private final RenderBuffers renderBuffers;
   private final FeatureRenderDispatcher featureRenderDispatcher;
   private final SubmitNodeStorage submitNodeStorage = new SubmitNodeStorage();
   private final ModelManager modelManager;
   private final TextureManager textureManager;
   private final AtlasManager atlasManager;
   private final ShaderManager shaderManager;
   private final LevelRenderState levelRenderState;
   private final OptionsRenderState optionsRenderState;
   private @Nullable SkyRenderer skyRenderer;
   private final CloudRenderer cloudRenderer = new CloudRenderer();
   private final WorldBorderRenderer worldBorderRenderer = new WorldBorderRenderer();
   private final WeatherEffectRenderer weatherEffectRenderer = new WeatherEffectRenderer();
   private final SectionOcclusionGraph sectionOcclusionGraph = new SectionOcclusionGraph();
   private final ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections = new ObjectArrayList(10000);
   private final ObjectArrayList<SectionRenderDispatcher.RenderSection> nearbyVisibleSections = new ObjectArrayList(50);
   private @Nullable ViewArea viewArea;
   private static final Logger MESH_LOGGER = LogUtils.getLogger();
   private final RenderTarget entityOutlineTarget;
   private final LevelTargetBundle targets = new LevelTargetBundle();
   private @Nullable SectionRenderDispatcher sectionRenderDispatcher;
   /** Phase C tuning (Workstream A2): max near/player-affected sections baked INLINE per frame
    *  when the worker pool is live; the rest offload to the pool to avoid the block-break freeze. */
   private static final int MAX_INLINE_SYNC_PER_FRAME = 1;
   /** The {@code BlockStateModelSet} identity last encoded for broadcast — a resource reload /
    *  atlas re-stitch produces a new instance, which is the exact signal to re-broadcast (H10).
    *  c87: also the idempotence guard for the bake-completion broadcast. */
   private net.minecraft.client.renderer.block.@Nullable BlockStateModelSet meshLastBroadcastModelSet;
   private @Nullable BlockPos lastTranslucentSortBlockPos;
   private int translucencyResortIterationIndex;
   private @Nullable GpuSampler chunkLayerSampler;
   private final SimpleGizmoCollector renderThreadGizmos = new SimpleGizmoCollector();
   private LevelRenderer.FinalizedGizmos finalizedGizmos = new LevelRenderer.FinalizedGizmos(new DrawableGizmoPrimitives(), new DrawableGizmoPrimitives());

   public LevelRenderer(
      final EntityRenderDispatcher entityRenderDispatcher,
      final BlockEntityRenderDispatcher blockEntityRenderDispatcher,
      final ModelManager modelManager,
      final TextureManager textureManager,
      final AtlasManager atlasManager,
      final ShaderManager shaderManager,
      final GameRenderer gameRenderer,
      final int width,
      final int height
   ) {
      this.gameRenderer = gameRenderer;
      this.entityRenderDispatcher = entityRenderDispatcher;
      this.blockEntityRenderDispatcher = blockEntityRenderDispatcher;
      this.renderBuffers = gameRenderer.renderBuffers();
      this.featureRenderDispatcher = gameRenderer.featureRenderDispatcher();
      this.modelManager = modelManager;
      this.textureManager = textureManager;
      this.atlasManager = atlasManager;
      this.shaderManager = shaderManager;
      this.levelRenderState = gameRenderer.gameRenderState().levelRenderState;
      this.optionsRenderState = gameRenderer.gameRenderState().optionsRenderState;
      this.entityOutlineTarget = new TextureTarget("Entity Outline", width, height, true, GpuFormat.RGBA8_UNORM);
   }

   public void render(
      final GraphicsResourceAllocator resourceAllocator,
      final DeltaTracker deltaTracker,
      final boolean renderOutline,
      final CameraRenderState cameraState,
      final Matrix4fc modelViewMatrix,
      final GpuBufferSlice terrainFog,
      final Vector4f fogColor,
      final boolean shouldRenderSky
   ) {
      boolean eaglerPerfEnabled = net.lax1dude.eaglercraft.v1_8.minecraft.EaglerClientPerf.isEnabled();
      long eaglerPhaseStarted = eaglerPerfEnabled ? Util.getNanos() : 0L;
      long eaglerSubmitNanos = 0L;
      long eaglerPrepareNanos = 0L;
      long eaglerChunkPrepareNanos = 0L;
      long eaglerExecuteNanos = 0L;
      long eaglerCompileNanos = 0L;
      long eaglerUploadNanos = 0L;
      int eaglerEntityCount = this.levelRenderState.entityRenderStates.size();
      int eaglerBlockEntityCount = this.levelRenderState.blockEntityRenderStates.size();
      float deltaPartialTick = deltaTracker.getGameTimeDeltaPartialTick(false);
      final ProfilerFiller profiler = Profiler.get();
      profiler.push("repositionCamera");
      this.repositionCamera(cameraState);
      Matrix4fStack modelViewStack = RenderSystem.getModelViewStack();
      modelViewStack.pushMatrix();
      modelViewStack.mul(modelViewMatrix);
      profiler.popPush("submitFeatures");
      this.submitFeatures(this.levelRenderState, this.submitNodeStorage, renderOutline);
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerSubmitNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }
      profiler.popPush("prepareFeatures");
      FeatureRenderDispatcher.PreparedFrame featureFrame = this.featureRenderDispatcher.prepareFrame(this.submitNodeStorage);
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerPrepareNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }
      profiler.popPush("setupFrameGraph");
      FrameGraphBuilder frame = new FrameGraphBuilder();
      this.targets.main = frame.importExternal("main", this.gameRenderer.mainRenderTarget());
      int screenWidth = this.gameRenderer.mainRenderTarget().width;
      int screenHeight = this.gameRenderer.mainRenderTarget().height;
      RenderTargetDescriptor screenSizeTargetDescriptor = new RenderTargetDescriptor(
         screenWidth, screenHeight, true, SCREEN_SIZE_TARGET_CLEAR_COLOR, GpuFormat.RGBA8_UNORM
      );
      PostChain transparencyChain = this.getTransparencyChain();
      if (transparencyChain != null) {
         this.targets.translucent = frame.createInternal("translucent", screenSizeTargetDescriptor);
         this.targets.itemEntity = frame.createInternal("item_entity", screenSizeTargetDescriptor);
         this.targets.particles = frame.createInternal("particles", screenSizeTargetDescriptor);
         this.targets.weather = frame.createInternal("weather", screenSizeTargetDescriptor);
         this.targets.clouds = frame.createInternal("clouds", screenSizeTargetDescriptor);
      }

      this.targets.entityOutline = frame.importExternal("entity_outline", this.entityOutlineTarget);
      FramePass clearPass = frame.addPass("clear");
      this.targets.main = clearPass.readsAndWrites(this.targets.main);
      clearPass.executes(
         () -> {
            RenderTarget mainRenderTarget = this.gameRenderer.mainRenderTarget();
            RenderSystem.getDevice()
               .createCommandEncoder()
               .clearColorAndDepthTextures(
                  mainRenderTarget.getColorTexture(), new Vector4f(fogColor.x, fogColor.y, fogColor.z, 0.0F), mainRenderTarget.getDepthTexture(), 0.0
               );
         }
      );
      if (shouldRenderSky) {
         this.addSkyPass(frame, cameraState, terrainFog);
      }

      ChunkSectionsToRender chunkSectionsToRender = this.prepareChunkRenders(this.levelRenderState.cameraRenderState.viewRotationMatrix);
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerChunkPrepareNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }
      this.addMainPass(frame, featureFrame, terrainFog, this.levelRenderState, profiler, chunkSectionsToRender);
      PostChain entityOutlineChain = this.shaderManager.getPostChain(ENTITY_OUTLINE_POST_CHAIN_ID, LevelTargetBundle.OUTLINE_TARGETS);
      if (featureFrame.hasAnyOutline() && entityOutlineChain != null) {
         entityOutlineChain.addToFrame(frame, screenWidth, screenHeight, this.targets);
      }

      CloudStatus cloudStatus = this.optionsRenderState.cloudStatus;
      if (cloudStatus != CloudStatus.OFF && ARGB.alpha(this.levelRenderState.cloudColor) > 0) {
         this.addCloudsPass(
            frame,
            cloudStatus,
            this.levelRenderState.cameraRenderState.pos,
            this.levelRenderState.gameTime,
            deltaPartialTick,
            this.levelRenderState.cloudColor,
            this.levelRenderState.cloudHeight,
            this.optionsRenderState.cloudRange
         );
      }

      this.addWeatherPass(frame, terrainFog);
      if (transparencyChain != null) {
         transparencyChain.addToFrame(frame, screenWidth, screenHeight, this.targets);
      }

      this.addAlwaysOnTopPass(frame, featureFrame, terrainFog);
      profiler.popPush("executeFrameGraph");
      frame.execute(resourceAllocator, new FrameGraphBuilder.Inspector() {
         @Override
         public void beforeExecutePass(final String name) {
            profiler.push(name);
         }

         @Override
         public void afterExecutePass(final String name) {
            profiler.pop();
         }
      });
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerExecuteNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }
      profiler.pop();
      this.targets.clear();
      modelViewStack.popMatrix();
      featureFrame.close();
	  if (this.sectionRenderDispatcher != null) {
		 this.sectionRenderDispatcher.onRenderFrameStart();
	  }
      profiler.push("compileSections");
      this.compileSections(cameraState);
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerCompileNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }
      profiler.pop();
      if (this.sectionRenderDispatcher != null) {
         // Mesh-worker plan Phase C: install any completed worker-baked sections on the render
         // thread, before the staging flush below makes them live (design doc §3.3/§3.4). No-op
         // when the worker pool is off/idle.
         this.sectionRenderDispatcher.drainWorkerMeshResults();
         this.sectionRenderDispatcher.lock();
         profiler.push("uploadTerrainBuffers");

         try {
            this.sectionRenderDispatcher.uploadTerrainBuffersToGpu();
         } finally {
            this.sectionRenderDispatcher.unlock();
         }

         profiler.pop();
      }
      if (eaglerPerfEnabled) {
         long now = Util.getNanos();
         eaglerUploadNanos = now - eaglerPhaseStarted;
         eaglerPhaseStarted = now;
      }

      profiler.push("updateSectionOcclusion");
      this.sectionOcclusionGraph.update(cameraState, this.optionsRenderState.fov, this.levelRenderState.chunkLoadingRenderState);
      profiler.pop();
      if (eaglerPerfEnabled) {
         net.lax1dude.eaglercraft.v1_8.minecraft.EaglerClientPerf.levelRender(
            eaglerSubmitNanos, eaglerPrepareNanos, eaglerChunkPrepareNanos, eaglerExecuteNanos,
            eaglerCompileNanos, eaglerUploadNanos, Util.getNanos() - eaglerPhaseStarted,
            this.visibleSections.size(), eaglerEntityCount, eaglerBlockEntityCount);
      }
      Runnable playerCompiledSectionCallback = this.levelRenderState.playerCompiledSectionCallback;
      if (playerCompiledSectionCallback != null && this.isSectionCompiledAndVisible(this.levelRenderState.cameraRenderState.blockPos)) {
         playerCompiledSectionCallback.run();
      }
   }

   private void submitFeatures(final LevelRenderState levelRenderState, final SubmitNodeCollector submitNodeCollector, final boolean renderOutline) {
      PoseStack poseStack = new PoseStack();
      this.submitEntities(poseStack, levelRenderState, submitNodeCollector);
      levelRenderState.entityRenderStates.clear();
      this.submitBlockEntities(poseStack, levelRenderState, submitNodeCollector);
      levelRenderState.blockEntityRenderStates.clear();
      this.submitBlockDestroyAnimation(poseStack, submitNodeCollector, levelRenderState);
      levelRenderState.blockBreakingRenderStates.clear();
      levelRenderState.particlesRenderState.submit(submitNodeCollector, levelRenderState.cameraRenderState);
      if (renderOutline) {
         this.submitBlockOutline(poseStack, this.submitNodeStorage, levelRenderState);
      }

      this.finalizeGizmoCollection();
      this.finalizedGizmos.standardPrimitives().submit(submitNodeCollector, levelRenderState.cameraRenderState, false);
      this.finalizedGizmos.alwaysOnTopPrimitives().submit(submitNodeCollector, levelRenderState.cameraRenderState, true);
      if (!levelRenderState.shouldShowEntityOutlines) {
         ObjectIterator var5 = this.submitNodeStorage.getSubmitsPerOrder().values().iterator();

         while (var5.hasNext()) {
            SubmitNodeCollection collection = (SubmitNodeCollection)var5.next();
            collection.outline.clear();
         }
      }

      this.checkPoseStack(poseStack);
   }

   private void repositionCamera(final CameraRenderState camera) {
      Vec3 cameraPos = camera.pos;
      SectionPos cameraSectionPos = SectionPos.of(cameraPos);
      if (this.viewArea.repositionCamera(cameraSectionPos)) {
         this.worldBorderRenderer.invalidate();
      }

      this.sectionRenderDispatcher.setCameraPosition(cameraPos);
   }

   private void addSkyPass(final FrameGraphBuilder frame, final CameraRenderState cameraState, final GpuBufferSlice skyFog) {
      FogType fogType = cameraState.fogType;
      if (fogType != FogType.POWDER_SNOW && fogType != FogType.LAVA && !cameraState.entityRenderState.doesMobEffectBlockSky) {
         if (this.levelRenderState.shouldResetSkyRenderer || this.skyRenderer == null) {
            if (this.skyRenderer != null) {
               this.skyRenderer.close();
            }

            this.skyRenderer = new SkyRenderer(this.textureManager, this.atlasManager, this.gameRenderer.mainRenderTarget());
         }

         SkyRenderState state = this.levelRenderState.skyRenderState;
         if (state.skybox != DimensionType.Skybox.NONE) {
            FramePass pass = frame.addPass("sky");
            this.targets.main = pass.readsAndWrites(this.targets.main);
            pass.executes(
               () -> {
                  RenderSystem.setShaderFog(skyFog);
                  if (state.skybox == DimensionType.Skybox.END) {
                     this.skyRenderer.renderEndSky();
                     if (state.endFlashIntensity > 1.0E-5F) {
                        PoseStack poseStack = new PoseStack();
                        this.skyRenderer.renderEndFlash(poseStack, state.endFlashIntensity, state.endFlashXAngle, state.endFlashYAngle);
                     }
                  } else {
                     PoseStack poseStack = new PoseStack();
                     this.skyRenderer.renderSkyDisc(state.skyColor);
                     this.skyRenderer.renderSunriseAndSunset(poseStack, state.sunAngle, state.sunriseAndSunsetColor);
                     this.skyRenderer
                        .renderSunMoonAndStars(
                           poseStack, state.sunAngle, state.moonAngle, state.starAngle, state.moonPhase, state.rainBrightness, state.starBrightness
                        );
                     if (state.shouldRenderDarkDisc) {
                        this.skyRenderer.renderDarkDisc();
                     }
                  }
               }
            );
         }
      }
   }

   private void addMainPass(
      final FrameGraphBuilder frame,
      final FeatureRenderDispatcher.PreparedFrame featureFrame,
      final GpuBufferSlice terrainFog,
      final LevelRenderState levelRenderState,
      final ProfilerFiller profiler,
      final ChunkSectionsToRender chunkSectionsToRender
   ) {
      FramePass pass = frame.addPass("main");
      this.targets.main = pass.readsAndWrites(this.targets.main);
      if (this.targets.translucent != null) {
         this.targets.translucent = pass.readsAndWrites(this.targets.translucent);
      }

      if (this.targets.itemEntity != null) {
         this.targets.itemEntity = pass.readsAndWrites(this.targets.itemEntity);
      }

      if (this.targets.weather != null) {
         this.targets.weather = pass.readsAndWrites(this.targets.weather);
      }

      if (this.targets.particles != null) {
         this.targets.particles = pass.readsAndWrites(this.targets.particles);
      }

      if (featureFrame.hasAnyOutline() && this.targets.entityOutline != null) {
         this.targets.entityOutline = pass.readsAndWrites(this.targets.entityOutline);
      }

      ResourceHandle<RenderTarget> mainTarget = this.targets.main;
      ResourceHandle<RenderTarget> translucentTarget = this.targets.translucent;
      ResourceHandle<RenderTarget> itemEntityTarget = this.targets.itemEntity;
      ResourceHandle<RenderTarget> entityOutlineTarget = this.targets.entityOutline;
      ResourceHandle<RenderTarget> particleTarget = this.targets.particles;
      pass.executes(
         () -> {
            RenderSystem.setShaderFog(terrainFog);
            if (levelRenderState.shouldResetChunkLayerSampler || this.chunkLayerSampler == null) {
               if (this.chunkLayerSampler != null) {
                  this.chunkLayerSampler.close();
               }

               int maxAnisotropy = this.optionsRenderState.textureFiltering == TextureFilteringMethod.ANISOTROPIC
                  ? this.optionsRenderState.maxAnisotropyValue
                  : 1;
               this.chunkLayerSampler = RenderSystem.getDevice()
                  .createSampler(
                     AddressMode.CLAMP_TO_EDGE, AddressMode.CLAMP_TO_EDGE, FilterMode.LINEAR, FilterMode.LINEAR, maxAnisotropy, OptionalDouble.empty()
                  );
            }

            profiler.push("solidTerrain");
            chunkSectionsToRender.renderGroup(ChunkSectionLayerGroup.OPAQUE, this.chunkLayerSampler);
            this.gameRenderer.lighting().setupFor(Lighting.Entry.LEVEL);
            if (levelRenderState.shouldShowEntityOutlines && entityOutlineTarget != null) {
               RenderTarget outlineTarget = entityOutlineTarget.get();
               RenderSystem.getDevice()
                  .createCommandEncoder()
                  .clearColorAndDepthTextures(outlineTarget.getColorTexture(), ENTITY_OUTLINE_CLEAR_COLOR, outlineTarget.getDepthTexture(), 0.0);
            }

            profiler.popPush("renderSolidFeatures");
            featureFrame.executeSolid();
            profiler.pop();
            if (translucentTarget != null) {
               translucentTarget.get().copyDepthFrom(mainTarget.get());
            }

            if (itemEntityTarget != null) {
               itemEntityTarget.get().copyDepthFrom(mainTarget.get());
            }

            if (particleTarget != null) {
               particleTarget.get().copyDepthFrom(mainTarget.get());
            }

            profiler.push("renderTranslucentFeatures");
            featureFrame.executeTranslucent();
            profiler.pop();
            featureFrame.executeOutline();
            profiler.push("translucentTerrain");
            chunkSectionsToRender.renderGroup(ChunkSectionLayerGroup.TRANSLUCENT, this.chunkLayerSampler);
            profiler.pop();
            featureFrame.executeTranslucentAfterTerrain();
         }
      );
   }

   private void addCloudsPass(
      final FrameGraphBuilder frame,
      final CloudStatus cloudStatus,
      final Vec3 cameraPosition,
      final long gameTime,
      final float partialTicks,
      final int cloudColor,
      final float cloudHeight,
      final int cloudRange
   ) {
      FramePass pass = frame.addPass("clouds");
      if (this.targets.clouds != null) {
         this.targets.clouds = pass.readsAndWrites(this.targets.clouds);
      } else {
         this.targets.main = pass.readsAndWrites(this.targets.main);
      }

      pass.executes(() -> this.cloudRenderer.render(cloudColor, cloudStatus, cloudHeight, cloudRange, cameraPosition, gameTime, partialTicks));
   }

   private void addWeatherPass(final FrameGraphBuilder frame, final GpuBufferSlice fog) {
      int renderDistance = this.optionsRenderState.renderDistance * 16;
      FramePass pass = frame.addPass("weather");
      if (this.targets.weather != null) {
         this.targets.weather = pass.readsAndWrites(this.targets.weather);
      } else {
         this.targets.main = pass.readsAndWrites(this.targets.main);
      }

      pass.executes(
         () -> {
            RenderSystem.setShaderFog(fog);
            CameraRenderState cameraState = this.levelRenderState.cameraRenderState;
            this.weatherEffectRenderer.render(cameraState.pos, this.levelRenderState.weatherRenderState);
            this.worldBorderRenderer
               .render(this.levelRenderState.worldBorderRenderState, cameraState.pos, renderDistance, this.levelRenderState.cameraRenderState.depthFar);
         }
      );
   }

   private void addAlwaysOnTopPass(final FrameGraphBuilder frame, final FeatureRenderDispatcher.PreparedFrame featureFrame, final GpuBufferSlice fog) {
      if (featureFrame.hasAnyAlwaysOnTop()) {
         FramePass pass = frame.addPass("always_on_top");
         this.targets.main = pass.readsAndWrites(this.targets.main);
         if (this.targets.itemEntity != null) {
            this.targets.itemEntity = pass.readsAndWrites(this.targets.itemEntity);
         }

         ResourceHandle<RenderTarget> mainTarget = this.targets.main;
         pass.executes(() -> {
            RenderSystem.setShaderFog(fog);
            PoseStack poseStack = new PoseStack();
            RenderTarget mainRenderTarget = mainTarget.get();
            RenderSystem.outputColorTextureOverride = mainRenderTarget.getColorTextureView();
            RenderSystem.outputDepthTextureOverride = mainRenderTarget.getDepthTextureView();
            RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(mainRenderTarget.getDepthTexture(), 0.0);
            featureFrame.executeAlwaysOnTop();
            RenderSystem.outputColorTextureOverride = null;
            RenderSystem.outputDepthTextureOverride = null;
            this.checkPoseStack(poseStack);
         });
      }
   }

   public ChunkSectionsToRender prepareChunkRenders(final Matrix4fc modelViewMatrix) {
      ObjectListIterator<SectionRenderDispatcher.RenderSection> iterator = this.visibleSections.listIterator(0);
      EnumMap<ChunkSectionLayer, Int2ObjectOpenHashMap<List<RenderPass.Draw<GpuBufferSlice[]>>>> drawGroups = new EnumMap<>(ChunkSectionLayer.class);
      int largestIndexCount = 0;

      for (ChunkSectionLayer layer : ChunkSectionLayer.values()) {
         drawGroups.put(layer, new Int2ObjectOpenHashMap());
      }

      List<DynamicUniforms.ChunkSectionInfo> sectionInfos = new ArrayList<>();
      GpuTextureView blockAtlas = this.textureManager.getTexture(TextureAtlas.LOCATION_BLOCKS).getTextureView();
      int textureAtlasWidth = blockAtlas.getWidth(0);
      int textureAtlasHeight = blockAtlas.getHeight(0);
      if (this.sectionRenderDispatcher != null) {
         this.sectionRenderDispatcher.lock();

         try {
            while (iterator.hasNext()) {
               SectionRenderDispatcher.RenderSection section = (SectionRenderDispatcher.RenderSection)iterator.next();
               SectionMesh sectionMesh = section.getSectionMesh();
               BlockPos renderOffset = section.getRenderOrigin();
               long now = Util.getMillis();
               int uboIndex = -1;

               for (ChunkSectionLayer layer : ChunkSectionLayer.values()) {
                  SectionMesh.SectionDraw draw = sectionMesh.getSectionDraw(layer);
                  SectionRenderDispatcher.RenderSectionBufferSlice slice = this.sectionRenderDispatcher.getRenderSectionSlice(sectionMesh, layer);
                  if (slice != null && draw != null && (!draw.hasCustomIndexBuffer() || slice.indexBuffer() != null)) {
                     if (uboIndex == -1) {
                        uboIndex = sectionInfos.size();
                        sectionInfos.add(
                           new DynamicUniforms.ChunkSectionInfo(
                              new Matrix4f(modelViewMatrix),
                              renderOffset.getX(),
                              renderOffset.getY(),
                              renderOffset.getZ(),
                              section.getVisibility(now),
                              textureAtlasWidth,
                              textureAtlasHeight
                           )
                        );
                     }

                     int combinedHash = 173;
                     VertexFormat vertexFormat = layer.pipeline().getVertexFormatBinding(0);
                     GpuBuffer vertexBuffer = slice.vertexBuffer();
                     if (layer != ChunkSectionLayer.TRANSLUCENT) {
                        combinedHash = 31 * combinedHash + vertexBuffer.hashCode();
                     }

                     int firstIndex = 0;
                     GpuBuffer indexBuffer;
                     IndexType indexType;
                     if (!draw.hasCustomIndexBuffer()) {
                        if (draw.indexCount() > largestIndexCount) {
                           largestIndexCount = draw.indexCount();
                        }

                        indexBuffer = null;
                        indexType = null;
                     } else {
                        indexBuffer = slice.indexBuffer();
                        indexType = draw.indexType();
                        if (layer != ChunkSectionLayer.TRANSLUCENT) {
                           combinedHash = 31 * combinedHash + indexBuffer.hashCode();
                           combinedHash = 31 * combinedHash + indexType.hashCode();
                        }

                        firstIndex = (int)(slice.indexBufferOffset() / indexType.bytes);
                     }

                     int finalUboIndex = uboIndex;
                     int baseVertex = (int)(slice.vertexBufferOffset() / vertexFormat.getVertexSize());
                     List<RenderPass.Draw<GpuBufferSlice[]>> draws = (List<RenderPass.Draw<GpuBufferSlice[]>>)drawGroups.get(layer)
                        .computeIfAbsent(combinedHash, var0 -> new ArrayList());
                     draws.add(
                        new RenderPass.Draw<>(
                           0,
                           vertexBuffer,
                           indexBuffer,
                           indexType,
                           firstIndex,
                           draw.indexCount(),
                           baseVertex,
                           (sectionUbos, uploader) -> uploader.upload("ChunkSection", sectionUbos[finalUboIndex])
                        )
                     );
                  }
               }
            }
         } finally {
            this.sectionRenderDispatcher.unlock();
         }
      }

      GpuBufferSlice[] chunkSectionInfos = RenderSystem.getDynamicUniforms().writeChunkSections(sectionInfos.toArray(new DynamicUniforms.ChunkSectionInfo[0]));
      return new ChunkSectionsToRender(blockAtlas, drawGroups, largestIndexCount, chunkSectionInfos);
   }

   private void compileSections(final CameraRenderState camera) {
      ProfilerFiller profiler = Profiler.get();
      profiler.push("populateSectionsToCompile");
      BlockPos cameraPosition = camera.blockPos;
      long fadeDuration = Mth.floor(this.optionsRenderState.chunkSectionFadeInTime * 1000.0);

      // Phase C tuning (Workstream A2): when the worker pool is live, compile at most one
      // player-affected section INLINE in a single frame — a block break dirties
      // the hit section plus its neighbours, and baking them all inline this frame is exactly the
      // reported freeze. The overflow is offloaded to the pool instead (its distance-priority poll
      // still serves these nearest sections first, so they land ~1-2 frames later, no stall). When
      // the pool is not ready this stays the stock inline path (nothing to offload to).
      boolean meshPoolReady = MeshWorkerRuntime.isReady();
      int inlineSyncThisFrame = 0;

      try {
         for (SectionUpdateRenderState state : this.levelRenderState.sectionUpdateRenderStates) {
            BlockPos center = SectionPos.of(state.sectionNode()).center();
            double distSqr = center.distSqr(cameraPosition);
            boolean isNearby = distSqr < 768.0;
            boolean rebuildSync = false;
            if (this.optionsRenderState.prioritizeChunkUpdates == PrioritizeChunkUpdates.NEARBY) {
               rebuildSync = isNearby || state.playerChanged();
            } else if (this.optionsRenderState.prioritizeChunkUpdates == PrioritizeChunkUpdates.PLAYER_AFFECTED) {
               rebuildSync = state.playerChanged();
            }

            SectionRenderDispatcher.RenderSection section = this.viewArea.getRenderSection(state.sectionNode());
            if (!isNearby && !section.wasPreviouslyEmpty()) {
               section.setFadeDuration(fadeDuration);
            } else {
               section.setFadeDuration(0L);
            }

            section.setWasPreviouslyEmpty(false);
            if (rebuildSync && (!meshPoolReady || (state.playerChanged() && inlineSyncThisFrame < MAX_INLINE_SYNC_PER_FRAME))) {
               profiler.push("compileSectionSynchronously");
               section.compileSync(state.region());
               profiler.pop();
               inlineSyncThisFrame++;
            } else if (state.playerChanged()) {
               // Player-affected fast path: block break/place rebuilds (and sync overflow beyond
               // the inline cap) are polled before normal terrain work, while retaining the same
               // bounded snapshot/upload budget so repeated edits cannot freeze entity ticks.
               section.compileAsyncPriority(state.region());
            } else {
               section.compileAsync(state.region());
            }
         }
      } finally {
         this.levelRenderState.sectionUpdateRenderStates.clear();
      }

      profiler.popPush("scheduleTranslucentResort");
      this.scheduleTranslucentSectionResort(camera.pos);
      profiler.pop();
   }

   private void checkPoseStack(final PoseStack poseStack) {
      if (!poseStack.isEmpty()) {
         throw new IllegalStateException("Pose stack not empty");
      }
   }

   private void submitEntities(final PoseStack poseStack, final LevelRenderState levelRenderState, final SubmitNodeCollector output) {
      Vec3 cameraPos = levelRenderState.cameraRenderState.pos;
      double camX = cameraPos.x();
      double camY = cameraPos.y();
      double camZ = cameraPos.z();

      for (EntityRenderState state : levelRenderState.entityRenderStates) {
         this.entityRenderDispatcher.submit(state, levelRenderState.cameraRenderState, state.x - camX, state.y - camY, state.z - camZ, poseStack, output);
      }
   }

   private void submitBlockEntities(final PoseStack poseStack, final LevelRenderState levelRenderState, final SubmitNodeCollector submitNodeCollector) {
      Vec3 cameraPos = levelRenderState.cameraRenderState.pos;
      double camX = cameraPos.x();
      double camY = cameraPos.y();
      double camZ = cameraPos.z();

      for (BlockEntityRenderState renderState : levelRenderState.blockEntityRenderStates) {
         BlockPos blockPos = renderState.blockPos;
         poseStack.pushPose();
         poseStack.translate(blockPos.getX() - camX, blockPos.getY() - camY, blockPos.getZ() - camZ);
         this.blockEntityRenderDispatcher.submit(renderState, poseStack, submitNodeCollector, levelRenderState.cameraRenderState);
         poseStack.popPose();
      }
   }

   private void submitBlockDestroyAnimation(final PoseStack poseStack, final SubmitNodeCollector submitNodeCollector, final LevelRenderState levelRenderState) {
      if (!levelRenderState.blockBreakingRenderStates.isEmpty()) {
         Vec3 cameraPos = levelRenderState.cameraRenderState.pos;
         double camX = cameraPos.x();
         double camY = cameraPos.y();
         double camZ = cameraPos.z();
         List<BlockStateModelPart> parts = new ArrayList<>();
         RandomSource random = RandomSource.createThreadLocalInstance();

         for (BlockBreakingRenderState state : levelRenderState.blockBreakingRenderStates) {
            if (state.blockState().getRenderShape() == RenderShape.MODEL) {
               BlockPos pos = state.blockPos();
               poseStack.pushPose();
               poseStack.translate(pos.getX() - camX, pos.getY() - camY, pos.getZ() - camZ);
               poseStack.translate(state.blockState().getOffset(pos));
               BlockStateModel model = this.modelManager.getBlockStateModelSet().get(state.blockState());
               random.setSeed(state.blockState().getSeed(pos));
               model.collectParts(random, parts);
               submitNodeCollector.submitBreakingBlockModel(poseStack, List.copyOf(parts), state.progress());
               parts.clear();
               poseStack.popPose();
            }
         }
      }
   }

   private void submitBlockOutline(final PoseStack poseStack, final SubmitNodeCollector submitNodeCollector, final LevelRenderState levelRenderState) {
      BlockOutlineRenderState state = levelRenderState.blockOutlineRenderState;
      if (state != null) {
         Vec3 cameraPos = levelRenderState.cameraRenderState.pos;
         BlockPos pos = state.pos();
         poseStack.pushPose();
         poseStack.translate(pos.getX() - cameraPos.x, pos.getY() - cameraPos.y, pos.getZ() - cameraPos.z);
         if (state.highContrast()) {
            this.submitHitOutline(poseStack, submitNodeCollector, RenderTypes.secondaryBlockOutline(), state, -16777216, 7.0F, state.isTranslucent());
         }

         int outlineColor = state.highContrast() ? -11010079 : ARGB.black(102);
         this.submitHitOutline(
            poseStack,
            submitNodeCollector,
            RenderTypes.lines(),
            state,
            outlineColor,
            this.gameRenderer.gameRenderState().windowRenderState.appropriateLineWidth,
            state.isTranslucent()
         );
         poseStack.popPose();
      }
   }

   private void submitHitOutline(
      final PoseStack poseStack,
      final SubmitNodeCollector submitNodeCollector,
      final RenderType renderType,
      final BlockOutlineRenderState state,
      final int color,
      final float width,
      final boolean afterTerrain
   ) {
      if (SharedConstants.DEBUG_SHAPES) {
         submitNodeCollector.submitShapeOutline(poseStack, state.shape(), renderType, -1, width, afterTerrain);
         if (state.collisionShape() != null) {
            submitNodeCollector.submitShapeOutline(
               poseStack, state.collisionShape(), renderType, ARGB.colorFromFloat(0.4F, 0.0F, 0.0F, 0.0F), width, afterTerrain
            );
         }

         if (state.occlusionShape() != null) {
            submitNodeCollector.submitShapeOutline(
               poseStack, state.occlusionShape(), renderType, ARGB.colorFromFloat(0.4F, 0.0F, 1.0F, 0.0F), width, afterTerrain
            );
         }

         if (state.interactionShape() != null) {
            submitNodeCollector.submitShapeOutline(
               poseStack, state.interactionShape(), renderType, ARGB.colorFromFloat(0.4F, 0.0F, 0.0F, 1.0F), width, afterTerrain
            );
         }
      } else {
         submitNodeCollector.submitShapeOutline(poseStack, state.shape(), renderType, color, width, afterTerrain);
      }
   }

   public void resize(final int width, final int height) {
      this.sectionOcclusionGraph.invalidate();
      this.entityOutlineTarget.resize(width, height);
   }

   public void endFrame() {
      this.cloudRenderer.endFrame();
   }

   @Override
   public void close() {
      this.resetLevelRenderData();
      this.entityOutlineTarget.destroyBuffers();
      if (this.skyRenderer != null) {
         this.skyRenderer.close();
      }

      if (this.chunkLayerSampler != null) {
         this.chunkLayerSampler.close();
      }

      this.worldBorderRenderer.close();
      this.cloudRenderer.close();
      this.weatherEffectRenderer.close();
   }

   public void doEntityOutline() {
      if (this.levelRenderState.shouldShowEntityOutlines) {
         this.entityOutlineTarget
            .blitAndBlendToTexture(this.gameRenderer.mainRenderTarget().getColorTextureView(), this.gameRenderer.mainRenderTarget().getDepthTextureView());
      }
   }

   public void invalidateCompiledGeometry(final ClientLevel level, final Options options, final Camera camera, final BlockColors blockColors) {
      SectionCompiler sectionCompiler = new SectionCompiler(
         options.ambientOcclusion().get(),
         options.cutoutLeaves().get(),
         this.modelManager.getBlockStateModelSet(),
         this.modelManager.getFluidStateModelSet(),
         blockColors
      );
      if (this.sectionRenderDispatcher == null) {
         this.sectionRenderDispatcher = new SectionRenderDispatcher(
            Util.backgroundExecutor(), this.renderBuffers, sectionCompiler, this.sectionOcclusionGraph::schedulePropagationFrom
         );
      } else {
         this.sectionRenderDispatcher.setCompiler(sectionCompiler);
      }

      this.cloudRenderer().markForRebuild();
      LeavesBlock.setCutoutLeaves(options.cutoutLeaves().get());
      // c87 fixes 1+2 — this is the BAKE-COMPLETION hook: the fresh BlockStateModelSet is in
      // hand here (during world-join setup, before the first in-world frame). Broadcast the
      // (tiny, appearing-states-only) base table NOW so the title-screen-prewarmed workers hit
      // table-ready within seconds of entering the world — the c86 regression was deferring
      // this behind 542 gameplay-frame slices + a cold worker spawn (~50 s of inline-only
      // meshing → empty world at spawn). Also the H10 reload path: a NEW model-set instance
      // (resource reload / atlas re-stitch) re-broadcasts under a bumped epoch.
      this.broadcastMeshWorkerTable();
      if (this.viewArea != null) {
         this.viewArea.releaseAllBuffers();
      }

      this.sectionRenderDispatcher.clearCompileQueue();
      this.viewArea = new ViewArea(
         this.sectionRenderDispatcher,
         level.getMinY(),
         level.getMaxY(),
         level.getMinSectionY(),
         level.getMaxSectionY(),
         options.getEffectiveRenderDistance(),
         this.sectionOcclusionGraph
      );
      this.sectionOcclusionGraph().waitAndReset(this.viewArea);
      this.clearVisibleSections();
      SectionPos cameraSectionPos = SectionPos.of(camera.position());
      this.viewArea.repositionCamera(cameraSectionPos);
   }

   /**
    * c87 fix 2 — broadcast the mesh-worker base model table at BAKE COMPLETION (called from
    * {@link #invalidateCompiledGeometry}). The base table is scoped to the EMPTY id set (~32 KB,
    * sub-ms encode — the 40.6 MB / 3.4 s full-registry table is gone entirely); models arrive in
    * the workers via lazy per-job table-DELTAS: {@code tryDispatchToWorker} calls
    * {@code MeshWorkerRuntime.ensureTableCoverage(palette)} which encodes just the
    * newly-appearing states through the encoder installed here. Cumulative delta traffic ≈ only
    * the states that actually appear in the world (typically a few hundred KB — the ~10x-plus
    * shrink), and workers hydrate the base in milliseconds. Also the H10 reload path: a NEW
    * {@code BlockStateModelSet} instance re-broadcasts under a bumped epoch (stale in-flight
    * results dropped, worker model maps reset, coverage reset). Idempotent per bake — guarded by
    * the model-set identity.
    */
   private void broadcastMeshWorkerTable() {
      if (!MeshWorkerRuntime.hasBridge()) {
         return;
      }
      final net.minecraft.client.renderer.block.BlockStateModelSet modelSet = this.modelManager.getBlockStateModelSet();
      if (modelSet == this.meshLastBroadcastModelSet && MeshWorkerRuntime.isStarted()) {
         return; // same bake is still installed (e.g. render-distance-only invalidate)
      }
      try {
         final int stateCount = Block.BLOCK_STATE_REGISTRY.size();
         boolean reload = MeshWorkerRuntime.isStarted();
         if (reload) {
            // H10: atlas re-stitch/model rebake — inline immediately (correct UVs) until re-ack.
            MeshWorkerRuntime.invalidateForReload("model/atlas rebake");
         }
         long t0 = System.nanoTime();
         byte[] table = ModelTableCodec.encode(modelSet, stateCount, java.util.Collections.emptySet(),
            this.modelManager.getFluidStateModelSet());
         double encodeMs = (System.nanoTime() - t0) / 1.0e6;
         // The per-job delta encoder captures THIS bake's model set; coverage restarts empty.
         MeshWorkerRuntime.installDeltaEncoder(
            ids -> ModelTableCodec.encode(modelSet, stateCount, ids), java.util.Collections.emptySet());
         this.meshLastBroadcastModelSet = modelSet;
         MESH_LOGGER.info(
            "[MeshWorkerPool] base model table (delta mode): {} bytes for {} states in {} ms — {}",
            table.length, stateCount, String.format(java.util.Locale.ROOT, "%.2f", encodeMs),
            reload ? "re-broadcasting (reload)" : "broadcasting");
         if (reload) {
            MeshWorkerRuntime.rebroadcastTable(table, encodeMs, "model/atlas rebake");
         } else {
            MeshWorkerRuntime.ensureStarted(table, encodeMs);
         }
      } catch (Throwable t) {
         MESH_LOGGER.error("[MeshWorkerPool] model-table broadcast failed; staying inline", t);
      }
   }

   private @Nullable PostChain getTransparencyChain() {
      return !this.gameRenderer.gameRenderState().useShaderTransparency()
         ? null
         : this.shaderManager.getPostChain(TRANSPARENCY_POST_CHAIN_ID, LevelTargetBundle.SORTING_TARGETS);
   }

   private void scheduleTranslucentSectionResort(final Vec3 cameraPos) {
      if (!this.visibleSections.isEmpty()) {
         BlockPos cameraBlockPos = BlockPos.containing(cameraPos);
         boolean blockPosChanged = !cameraBlockPos.equals(this.lastTranslucentSortBlockPos);
         TranslucencyPointOfView pointOfView = new TranslucencyPointOfView();
         ObjectListIterator resortsLeft = this.nearbyVisibleSections.iterator();

         while (resortsLeft.hasNext()) {
            SectionRenderDispatcher.RenderSection section = (SectionRenderDispatcher.RenderSection)resortsLeft.next();
            this.scheduleResort(section, pointOfView, cameraPos, blockPosChanged, true);
         }

         this.translucencyResortIterationIndex = this.translucencyResortIterationIndex % this.visibleSections.size();
         int resortsLeftx = Math.max(this.visibleSections.size() / 8, 15);

         while (resortsLeftx-- > 0) {
            int index = this.translucencyResortIterationIndex++ % this.visibleSections.size();
            this.scheduleResort((SectionRenderDispatcher.RenderSection)this.visibleSections.get(index), pointOfView, cameraPos, blockPosChanged, false);
         }

         this.lastTranslucentSortBlockPos = cameraBlockPos;
      }
   }

   private void scheduleResort(
      final SectionRenderDispatcher.RenderSection section,
      final TranslucencyPointOfView pointOfView,
      final Vec3 cameraPos,
      final boolean blockPosChanged,
      final boolean isNearby
   ) {
      pointOfView.set(cameraPos, section.getSectionNode());
      boolean pointOfViewChanged = section.getSectionMesh().isDifferentPointOfView(pointOfView);
      boolean resortBecauseBlockPosChanged = blockPosChanged && (pointOfView.isAxisAligned() || isNearby);
      if ((resortBecauseBlockPosChanged || pointOfViewChanged) && !section.transparencyResortingScheduled() && section.hasTranslucentGeometry()) {
         section.resortTransparency();
      }
   }

   public void clearVisibleSections() {
      this.visibleSections.clear();
      this.nearbyVisibleSections.clear();
   }

   public void releaseMeshModelReference() {
      // A title-screen pack reload must not keep the previous world's bake alive.
      // Final disconnect calls this; server transfers retain the broadcast guard.
      this.meshLastBroadcastModelSet = null;
   }

   public void resetLevelRenderData() {
      if (this.viewArea != null) {
         this.viewArea.releaseAllBuffers();
         this.viewArea = null;
      }

      if (this.sectionRenderDispatcher != null) {
         this.sectionRenderDispatcher.dispose();
      }

      this.sectionRenderDispatcher = null;
      this.sectionOcclusionGraph.waitAndReset(null);
      this.clearVisibleSections();
   }

   public void eaglerTrimAfterWorld() {
      this.renderBuffers.eaglerTrimAfterWorld();
   }

   public void eaglerBeginTrimAfterWorld() {
      this.renderBuffers.eaglerBeginTrimAfterWorld();
   }

   public void eaglerCancelTrimAfterWorld() {
      this.renderBuffers.eaglerCancelTrimAfterWorld();
   }

   public void eaglerTrimAfterWorldStep() {
      this.renderBuffers.eaglerTrimAfterWorldStep();
   }

   public boolean hasRenderedAllSections() {
      return this.sectionRenderDispatcher == null || this.sectionRenderDispatcher.isQueueEmpty();
   }

   public boolean isSectionCompiledAndVisible(final BlockPos blockPos) {
      if (this.viewArea == null) {
         return false;
      }

      SectionRenderDispatcher.RenderSection renderSection = this.viewArea.getRenderSectionAt(blockPos);
      return renderSection != null && renderSection.sectionMesh.get() != CompiledSectionMesh.UNCOMPILED
         ? renderSection.getVisibility(Util.getMillis()) >= 0.3F
         : false;
   }

   /** The initial browser loading screen is an opaque panorama, so its occlusion visibility
    *  may not advance. A non-UNCOMPILED center section still proves that terrain geometry is
    *  installed and ready for the next GPU upload. */
   public boolean isSectionCompiled(final BlockPos blockPos) {
      if (this.viewArea == null) {
         return false;
      }

      SectionRenderDispatcher.RenderSection renderSection = this.viewArea.getRenderSectionAt(blockPos);
      return renderSection != null && renderSection.sectionMesh.get() != CompiledSectionMesh.UNCOMPILED;
   }

   public @Nullable SectionRenderDispatcher sectionRenderDispatcher() {
      return this.sectionRenderDispatcher;
   }

   public EntityRenderDispatcher entityRenderDispatcher() {
      return this.entityRenderDispatcher;
   }

   public BlockEntityRenderDispatcher blockEntityRenderDispatcher() {
      return this.blockEntityRenderDispatcher;
   }

   public @Nullable RenderTarget entityOutlineTarget() {
      return this.targets.entityOutline != null ? this.targets.entityOutline.get() : null;
   }

   public @Nullable RenderTarget translucentTarget() {
      return this.targets.translucent != null ? this.targets.translucent.get() : null;
   }

   public @Nullable RenderTarget itemEntityTarget() {
      return this.targets.itemEntity != null ? this.targets.itemEntity.get() : null;
   }

   public @Nullable RenderTarget particlesTarget() {
      return this.targets.particles != null ? this.targets.particles.get() : null;
   }

   public @Nullable RenderTarget weatherTarget() {
      return this.targets.weather != null ? this.targets.weather.get() : null;
   }

   public @Nullable RenderTarget cloudsTarget() {
      return this.targets.clouds != null ? this.targets.clouds.get() : null;
   }

   public CloudRenderer cloudRenderer() {
      return this.cloudRenderer;
   }

   public @Nullable SkyRenderer skyRenderer() {
      return this.skyRenderer;
   }

   public WeatherEffectRenderer weatherEffectRenderer() {
      return this.weatherEffectRenderer;
   }

   public WorldBorderRenderer worldBorderRenderer() {
      return this.worldBorderRenderer;
   }

   public @Nullable ViewArea viewArea() {
      return this.viewArea;
   }

   public ObjectArrayList<SectionRenderDispatcher.RenderSection> visibleSections() {
      return this.visibleSections;
   }

   public ObjectArrayList<SectionRenderDispatcher.RenderSection> nearbyVisibleSections() {
      return this.nearbyVisibleSections;
   }

   public LongCollection expectedChunks() {
      return this.sectionOcclusionGraph.expectedChunks();
   }

   public SectionOcclusionGraph sectionOcclusionGraph() {
      return this.sectionOcclusionGraph;
   }

   public Gizmos.TemporaryCollection collectPerFrameRenderThreadGizmos() {
      return Gizmos.withCollector(this.renderThreadGizmos);
   }

   private void finalizeGizmoCollection() {
      DrawableGizmoPrimitives standardPrimitives = new DrawableGizmoPrimitives();
      DrawableGizmoPrimitives alwaysOnTopPrimitives = new DrawableGizmoPrimitives();
      long currentMillis = Util.getMillis();

      for (SimpleGizmoCollector.GizmoInstance instance : this.renderThreadGizmos.drainGizmos()) {
         instance.gizmo().emit(instance.isAlwaysOnTop() ? alwaysOnTopPrimitives : standardPrimitives, instance.getAlphaMultiplier(currentMillis));
      }

      this.finalizedGizmos = new LevelRenderer.FinalizedGizmos(standardPrimitives, alwaysOnTopPrimitives);
   }

   public void addMainThreadGizmos(final List<SimpleGizmoCollector.GizmoInstance> mainThreadGizmos) {
      this.renderThreadGizmos.addTemporaryGizmos(mainThreadGizmos);
   }

   private record FinalizedGizmos(DrawableGizmoPrimitives standardPrimitives, DrawableGizmoPrimitives alwaysOnTopPrimitives) {
   }
}
