mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-08 12:35:01 +00:00
Update to Celeritas 2.5.12 with translucency sorting v3 (#2151)
This commit is contained in:
1 parent
a8c29fa3f9
commit
c9d46e70e0
50 files changed
+870
-556
No files matched your search
@@ -9,7 +9,7 @@ asmCompile = "5.0.3"
|
||||
lwjgl3 = "3.4.2-SNAPSHOT"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.11-GTNH"
|
||||
celeritas = "2.5.12-GTNH"
|
||||
gtnhlib = "0.11.36"
|
||||
eventbus = "7.0-beta.15"
|
||||
glslTransformLib = "0.2.0-33.g3ec381e"
|
||||
|
||||
@@ -542,11 +542,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void onRenderThreadReleased(Thread t) {
|
||||
if (shutdown || t == GLStateManager.getMainThread()) return;
|
||||
final ContextState st = tlState.get();
|
||||
if (frameManager.isFrameActive()) {
|
||||
if (st != null) fboClearTracker.materializeAllPendingClears(st);
|
||||
frameManager.endFrame();
|
||||
}
|
||||
frameManager.releaseThreadState();
|
||||
final ContextState st = tlState.get();
|
||||
if (st != null) {
|
||||
tlState.remove();
|
||||
registeredStates.remove(st);
|
||||
@@ -601,6 +602,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public boolean handleSwapBuffers() {
|
||||
final OffscreenTarget splash = splashTarget;
|
||||
if (splash != null && splash.isFor(s()) && Thread.currentThread() != GLStateManager.getMainThread()) {
|
||||
if (frameManager.isFrameActive()) fboClearTracker.materializeAllPendingClears(s());
|
||||
endFrameUploadFlush();
|
||||
frameManager.endFrame();
|
||||
SplashDispatcher.signalFrameReady((int) s().viewportW, (int) s().viewportH);
|
||||
@@ -728,6 +730,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
frameManager.markFrameEmpty(f);
|
||||
}
|
||||
|
||||
if (f.frameActive) fboClearTracker.materializeAllPendingClears(s());
|
||||
awaitUploadFlush();
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.frameEnd(f);
|
||||
frameManager.endFrame();
|
||||
@@ -740,6 +743,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void onPreSwapchainInvalidatingChange(Object change) {
|
||||
super.onPreSwapchainInvalidatingChange(change);
|
||||
if (presenter != null) presenter.drain();
|
||||
if (frameManager.isFrameActive()) fboClearTracker.materializeAllPendingClears(s());
|
||||
endFrameUploadFlush();
|
||||
if (frameManager.isFrameActive()) {
|
||||
frameManager.endFrame();
|
||||
|
||||
@@ -283,6 +283,19 @@ public final class FBOClearTracker {
|
||||
}
|
||||
}
|
||||
|
||||
public void materializeAllPendingClears(ContextState st) {
|
||||
if (st.pendingColorTextures.isEmpty() && st.pendingDepthTextures.isEmpty() && st.pendingStencilTextures.isEmpty()) return;
|
||||
final LongArrayList handles = st.samplerFlushColorHandles;
|
||||
handles.clear();
|
||||
handles.addAll(st.pendingColorTextures);
|
||||
handles.addAll(st.pendingDepthTextures);
|
||||
handles.addAll(st.pendingStencilTextures);
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
materializePendingClearForTexture(st, handles.getLong(i));
|
||||
}
|
||||
handles.clear();
|
||||
}
|
||||
|
||||
public void scrubPendingClearsForTexture(ContextState st, int glId) {
|
||||
final long handle = resourceManager.getTextureHandle(glId);
|
||||
if (handle == 0) return;
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ class GlsmSdlSplashTargetTest {
|
||||
final OffscreenTarget target = createSplashTarget(backend);
|
||||
Reflect.set(backend, "splashTarget", target);
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
TestThreads.run("SplashTarget-Seed-Thread", () -> {
|
||||
Reflect.invoke(backend, "enterContext", new Class<?>[]{boolean.class}, true);
|
||||
GlsmSdlHeadlessRig.solidQuad(0f, 1f, 0f);
|
||||
|
||||
@@ -5,6 +5,8 @@ import org.embeddedt.embeddium.impl.render.chunk.fog.FogService;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkFogMode;
|
||||
|
||||
public class AngelicaFogService implements FogService {
|
||||
public static final AngelicaFogService INSTANCE = new AngelicaFogService();
|
||||
|
||||
private final float[] fogColorArray = new float[4];
|
||||
|
||||
@Override
|
||||
|
||||
@@ -419,6 +419,10 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean disableF3Additions;
|
||||
|
||||
@Config.Comment("Shows developer counters (FFP, streaming, TESR, transfer) on the F3 screen. Toggle in game with F3+V.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean verboseF3;
|
||||
|
||||
@Config.Comment("Replaces various FFP uploads with statically allocated VBO's.")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean replaceFFPUploads;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizons.angelica.debug;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.GTNHLib;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
/** F3+V toggles {@link AngelicaConfig#verboseF3} */
|
||||
public class DebugKeyHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKey(InputEvent.KeyInputEvent event) {
|
||||
if (!Keyboard.getEventKeyState() || Keyboard.getEventKey() != Keyboard.KEY_V || !Keyboard.isKeyDown(Keyboard.KEY_F3)
|
||||
|| Minecraft.getMinecraft().currentScreen != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AngelicaConfig.verboseF3 = !AngelicaConfig.verboseF3;
|
||||
ConfigurationManager.save(AngelicaConfig.class);
|
||||
CeleritasDebugScreenHandler.invalidate();
|
||||
GTNHLib.proxy.addDebugToChat(StatCollector.translateToLocal(AngelicaConfig.verboseF3 ? "angelica.debug.verbose.on" : "angelica.debug.verbose.off"));
|
||||
}
|
||||
}
|
||||
@@ -419,7 +419,9 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.terrain.MixinWorld_AwaitingDescriptor"
|
||||
, "celeritas.terrain.MixinRenderRegion"
|
||||
, "celeritas.terrain.MixinSectionRenderDataStorage"
|
||||
, "celeritas.terrain.MixinDefaultChunkShaderInterface"
|
||||
, "celeritas.terrain.MixinSharedQuadIndexBuffer"
|
||||
, "celeritas.terrain.MixinRenderRegionManager"
|
||||
, "celeritas.terrain.MixinShaderLoader"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
-3
@@ -1,14 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkTaskOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.executor.ChunkJobResult;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
public interface RenderSectionManagerAccessor {
|
||||
Long2ReferenceMap<RenderSection> angelica$getSectionByPosition();
|
||||
ConcurrentLinkedDeque<Runnable> angelica$getAsyncSubmittedTasks();
|
||||
ConcurrentLinkedDeque<ChunkJobResult<? extends ChunkTaskOutput>> angelica$getBuildResults();
|
||||
}
|
||||
@@ -22,8 +22,6 @@ import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import net.coderbot.iris.pipeline.ShadowRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.SharedQuadIndexBuffer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.region.RenderRegionManager;
|
||||
|
||||
public final class TracyFramePlots {
|
||||
private static final long P_ALLOC_RATE = Tracy.plotHandle("allocRate", TracyBackend.PLOT_FORMAT_MEMORY);
|
||||
@@ -267,9 +265,9 @@ public final class TracyFramePlots {
|
||||
TESR_BAILS[reason.ordinal()].plot(batcher.statBail(reason));
|
||||
}
|
||||
|
||||
P_MESH_UPLOADED_SECTIONS.plot(RenderRegionManager.getSectionsUploaded());
|
||||
P_MESH_UPLOADED_BYTES.plot(RenderRegionManager.getBytesUploaded());
|
||||
P_DRAW_INDEX_BUFFER_GROWTHS.plot(SharedQuadIndexBuffer.getGrowths());
|
||||
P_MESH_UPLOADED_SECTIONS.plot(TerrainDrawStats.getSectionsUploaded());
|
||||
P_MESH_UPLOADED_BYTES.plot(TerrainDrawStats.getBytesUploaded());
|
||||
P_DRAW_INDEX_BUFFER_GROWTHS.plot(TerrainDrawStats.getIndexBufferGrowths());
|
||||
Tracy.plotInt(P_MESH_META_SLOTS, GpuCulling.sectionMeta().getHighWaterMark());
|
||||
Tracy.plotInt(P_MT_QUEUE_DEPTH, AngelicaRenderQueue.getQueueDepth());
|
||||
Tracy.plotInt(P_MT_QUEUE_TASKS, AngelicaRenderQueue.getLastFrameTasksRan());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.proxy;
|
||||
|
||||
import com.gtnewhorizons.angelica.debug.DebugKeyHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
|
||||
|
||||
@@ -185,6 +186,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
Zoom.init();
|
||||
}
|
||||
FpsReducer.init();
|
||||
FMLCommonHandler.instance().bus().register(new DebugKeyHandler());
|
||||
AngelicaConfig.applyGpuCullingMode();
|
||||
if (AngelicaConfig.enableDynamicLights) {
|
||||
EntityLightConfig.init(new java.io.File(mc.mcDataDir, "config"));
|
||||
|
||||
+2
-2
@@ -299,7 +299,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
final Reference2ReferenceMap<TerrainRenderPass, BuiltSectionMeshParts> meshes;
|
||||
try {
|
||||
meshes = BuiltSectionMeshParts.groupFromBuildBuffers(buffers,
|
||||
(float) camera.x - minX, (float) camera.y - minY, (float) camera.z - minZ);
|
||||
(float) (camera.x - minX), (float) (camera.y - minY), (float) (camera.z - minZ));
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
@@ -399,7 +399,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_MESH_FINALIZE);
|
||||
final Reference2ReferenceMap<TerrainRenderPass, BuiltSectionMeshParts> meshes;
|
||||
try {
|
||||
meshes = BuiltSectionMeshParts.groupFromBuildBuffers(m.buffers, (float) camera.x - minX, (float) camera.y - minY, (float) camera.z - minZ);
|
||||
meshes = BuiltSectionMeshParts.groupFromBuildBuffers(m.buffers, (float) (camera.x - minX), (float) (camera.y - minY), (float) (camera.z - minZ));
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
|
||||
+10
-13
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.rendering.AngelicaFogService;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
@@ -27,7 +28,6 @@ import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlShader;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderParser;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderType;
|
||||
import org.embeddedt.embeddium.impl.gl.tessellation.GlPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.gl.tessellation.GlTessellation;
|
||||
@@ -81,7 +81,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
private static MultiDrawMode installedBatchMode;
|
||||
|
||||
public AngelicaChunkRenderer(RenderDevice device, RenderPassConfiguration<?> renderPassConfiguration) {
|
||||
super(device, renderPassConfiguration);
|
||||
super(device, renderPassConfiguration, AngelicaFogService.INSTANCE);
|
||||
|
||||
installBatchFactory();
|
||||
this.culler = createCuller();
|
||||
@@ -148,11 +148,6 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static GlShader loadShader(ShaderType type, String path, ShaderConstants constants) {
|
||||
final String source = ShaderParser.parseShader(ShaderLoader.getShaderSource(path), ShaderLoader::getShaderSource, constants);
|
||||
return new GlShader(type, path, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void begin(TerrainRenderPass pass) {
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_CHUNK_BEGIN);
|
||||
@@ -301,8 +296,8 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
final List<GlShader> loadedShaders = new ArrayList<>();
|
||||
|
||||
try {
|
||||
loadedShaders.add(loadShader(ShaderType.VERTEX, "sodium:" + path + ".vsh", constants));
|
||||
loadedShaders.add(loadShader(ShaderType.FRAGMENT, "angelica:" + path + ".fsh", constants));
|
||||
loadedShaders.add(ShaderLoader.loadShader(ShaderType.VERTEX, "sodium:" + path + ".vsh", constants));
|
||||
loadedShaders.add(ShaderLoader.loadShader(ShaderType.FRAGMENT, "angelica:" + path + ".fsh", constants));
|
||||
|
||||
final var builder = GlProgram.builder("sodium:chunk_shader");
|
||||
loadedShaders.forEach(builder::attachShader);
|
||||
@@ -310,8 +305,10 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
for (var attr : options.pass().vertexType().getVertexFormat().getAttributes()) {
|
||||
builder.bindAttribute(attr.getName(), i++);
|
||||
}
|
||||
builder.bindFragmentData("fragColor", ChunkShaderBindingPoints.FRAG_COLOR);
|
||||
return builder.link((shader) -> new DefaultChunkShaderInterface(shader, options));
|
||||
if (!this.enableLegacyGLPatches) {
|
||||
builder.bindFragmentData("fragColor", ChunkShaderBindingPoints.FRAG_COLOR);
|
||||
}
|
||||
return builder.link((shader) -> new DefaultChunkShaderInterface(shader, options, this.environment));
|
||||
} finally {
|
||||
loadedShaders.forEach(GlShader::delete);
|
||||
}
|
||||
@@ -351,12 +348,12 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
|
||||
useBlockFaceCulling, cacheParams);
|
||||
}
|
||||
|
||||
final long batchesCreatedBefore = BatchAssembler.getCachedBatchesCreated();
|
||||
final CachedBatch before = storage.getCachedMultiDrawBatch(cacheParams);
|
||||
Tracy.beginZone(Z_CHUNK_ASSEMBLE_REGION);
|
||||
try {
|
||||
final CachedBatch cached = super.getRegionBatch(commandList, region, storage, renderList, occlusionCamera,
|
||||
renderPass, useBlockFaceCulling, cacheParams);
|
||||
if (BatchAssembler.getCachedBatchesCreated() != batchesCreatedBefore) TerrainDrawStats.recordRebuild();
|
||||
if (cached != before) TerrainDrawStats.recordRebuild();
|
||||
final MultiDrawBatch batch = cached != null ? cached.getBatch() : null;
|
||||
if (batch != null && !batch.isEmpty()) TerrainDrawStats.recordBatch(batch.size());
|
||||
return cached;
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Adapated from: Beddium for usage in Angelica
|
||||
*
|
||||
* Copyright (C) 2025 Ven, FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkStatus;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTrackerImpl;
|
||||
import org.embeddedt.embeddium.impl.util.PositionUtil;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
/**
|
||||
* Configurable chunk tracker supporting "fast" and "aggressive" loading modes.
|
||||
* Based on Beddium's SafeChunkTracker by Ven and FalsePattern.
|
||||
*
|
||||
* Modes (controlled by {@link AngelicaConfig#useVanillaChunkTracking}):
|
||||
* - Fast (default): Only renders chunks when all 8 neighbors are loaded (better lighting/AO)
|
||||
* - Aggressive: Renders chunks immediately when loaded (faster perceived loading)
|
||||
*
|
||||
* Mode can be switched at runtime via options menu - triggers renderer reload.
|
||||
*/
|
||||
public class AngelicaChunkTracker extends ChunkTrackerImpl {
|
||||
|
||||
@Override
|
||||
public void forEachReady(RenderSectionManager sectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
super.forEachReady(sectionManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEvent(RenderSectionManager sectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
}
|
||||
|
||||
super.forEachEvent(sectionManager);
|
||||
}
|
||||
}
|
||||
+25
-16
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
import com.gtnewhorizons.angelica.utils.SpritePadding;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import lombok.Getter;
|
||||
@@ -15,6 +16,7 @@ import org.embeddedt.embeddium.impl.render.chunk.terrain.material.parameters.Alp
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AngelicaRenderPassConfiguration {
|
||||
@@ -31,25 +33,32 @@ public class AngelicaRenderPassConfiguration {
|
||||
.vertexType(vertexType)
|
||||
.primitiveType(QuadPrimitiveType.TRIANGULATED);
|
||||
|
||||
final int mipmapLevels = SodiumGameOptions.terrainMipmapLevels();
|
||||
|
||||
if (rgssEnabled) {
|
||||
builder.extraDefine("USE_RGSS", "");
|
||||
}
|
||||
if (SodiumGameOptions.usesTerrainTexelSnap()) {
|
||||
builder.extraDefine("USE_TEXEL_SNAP", "");
|
||||
}
|
||||
if (SodiumGameOptions.effectiveTextureFilterMode().usesAnisotropy()) {
|
||||
builder.extraDefine("USE_ANISOTROPIC", "");
|
||||
builder.extraDefine("TERRAIN_GUTTER", SpritePadding.gutterFor(mipmapLevels, true) + ".0");
|
||||
}
|
||||
if (mipmapLevels <= 0) {
|
||||
builder.extraDefine("TERRAIN_NO_MIPS", "");
|
||||
}
|
||||
samplerDefines(rgssEnabled, SodiumGameOptions.usesTerrainTexelSnap(),
|
||||
SodiumGameOptions.effectiveTextureFilterMode().usesAnisotropy(), SodiumGameOptions.terrainMipmapLevels())
|
||||
.forEach(builder::extraDefine);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/** Defines selecting the terrain fragment shader's sampling path. */
|
||||
public static Map<String, String> samplerDefines(boolean rgss, boolean texelSnap, boolean anisotropic, int mipmapLevels) {
|
||||
final Map<String, String> defines = new LinkedHashMap<>();
|
||||
if (rgss) {
|
||||
defines.put("USE_RGSS", "");
|
||||
}
|
||||
if (texelSnap) {
|
||||
defines.put("USE_TEXEL_SNAP", "");
|
||||
}
|
||||
if (anisotropic) {
|
||||
defines.put("USE_ANISOTROPIC", "");
|
||||
defines.put("TERRAIN_GUTTER", SpritePadding.gutterFor(mipmapLevels, true) + ".0");
|
||||
}
|
||||
if (mipmapLevels <= 0) {
|
||||
defines.put("TERRAIN_NO_MIPS", "");
|
||||
}
|
||||
return defines;
|
||||
}
|
||||
|
||||
public static RenderPassConfiguration<BlockRenderLayer> build(ChunkVertexType vertexType) {
|
||||
rgssEnabled = SodiumGameOptions.effectiveTextureFilterMode().usesRgss();
|
||||
|
||||
@@ -69,7 +78,7 @@ public class AngelicaRenderPassConfiguration {
|
||||
.name("translucent")
|
||||
.fragmentDiscard(false)
|
||||
.useReverseOrder(true)
|
||||
.useTranslucencySorting(true)
|
||||
.useTranslucencySorting(ClientProxy.options().performance.translucencySorting)
|
||||
.build();
|
||||
|
||||
TRANSLUCENT_MATERIAL = new Material(TRANSLUCENT_PASS, AlphaCutoffParameter.ZERO, true);
|
||||
|
||||
+17
-46
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.cardinalstar.cubicchunks.world.ICubicWorld;
|
||||
import com.gtnewhorizons.angelica.client.rendering.AngelicaFogService;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
@@ -20,22 +21,21 @@ import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkRenderMatrices;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkUpdateType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.tasks.ChunkBuilderTask;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.fog.FogService;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.lists.SectionTicker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.sprite.GenericSectionSpriteTicker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.CameraTransform;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.embeddedt.embeddium.impl.util.PositionUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -58,15 +58,13 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
private static final Tracy.ZoneId Z_BIOME_REBUILDS = Tracy.zoneId("biomeRebuilds", Tracy.COLOR_TERRAIN);
|
||||
private static final Tracy.ZoneId Z_GRAPH_SEARCH = Tracy.zoneId("graphSearch", Tracy.COLOR_TERRAIN);
|
||||
private static final Tracy.ZoneId Z_SECTION_UPLOAD = Tracy.zoneId("sectionUpload", Tracy.COLOR_TERRAIN);
|
||||
private static final Tracy.ZoneId Z_SHADOW_GRAPH_WAIT = Tracy.zoneId("shadowGraphWait", Tracy.COLOR_TERRAIN);
|
||||
|
||||
private boolean initialCameraSectionReady = false;
|
||||
|
||||
private final WorldClient world;
|
||||
private final ClonedChunkSectionCache sectionCache;
|
||||
private final ChunkTaskProvider taskProvider;
|
||||
private final LongOpenHashSet biomeRebuildColumns = new LongOpenHashSet();
|
||||
private final DeferredMeshScheduler deferredScheduler;
|
||||
private int shadowSearchSubmissions;
|
||||
|
||||
public AngelicaRenderSectionManager(RenderPassConfiguration<?> configuration, WorldClient world, int renderDistance, CommandList commandList, int minSection, int maxSection, int requestedThreads, ChunkTaskProvider taskProvider) {
|
||||
super(configuration, () -> new AngelicaChunkBuildContext(configuration, world), AngelicaChunkRenderer::new, renderDistance, commandList, minSection, maxSection, requestedThreads, true /* hasShadowPass = true for Iris */);
|
||||
@@ -92,24 +90,9 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
return new AngelicaRenderSectionManager(AngelicaRenderPassConfiguration.build(vertexType), world, renderDistance, commandList, minSection, maxSection, provider.threadCount(), provider);
|
||||
}
|
||||
|
||||
public void setCameraPosition(double x, double y, double z) {
|
||||
this.cameraPosition.set(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Viewport positionedViewport, int frame, boolean spectator) {
|
||||
if (isInShadowPass()) {
|
||||
return;
|
||||
}
|
||||
if (!initialCameraSectionReady) {
|
||||
var origin = positionedViewport.getChunkCoord();
|
||||
long key = PositionUtil.packSection(origin.x(), origin.y(), origin.z());
|
||||
if (!((RenderSectionManagerAccessor) this).angelica$getSectionByPosition().containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
initialCameraSectionReady = true;
|
||||
}
|
||||
super.update(positionedViewport, frame, spectator);
|
||||
public FogService getFogService() {
|
||||
return AngelicaFogService.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,7 +156,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ChunkBuilderTask<ChunkBuildOutput> task = this.taskProvider.createRebuildTask(render, frame, this.cameraPosition, this.sectionCache);
|
||||
final ChunkBuilderTask<ChunkBuildOutput> task = this.taskProvider.createRebuildTask(render, frame, new Vector3d(this.cameraPosition), this.sectionCache);
|
||||
if (task instanceof AngelicaChunkBuilderMeshingTask t) {
|
||||
final ChunkUpdateType pending = render.getPendingUpdate();
|
||||
t.bindScheduler(this.deferredScheduler, pending != null && pending.isImportant());
|
||||
@@ -219,9 +202,6 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
|
||||
@Override
|
||||
public void updateChunks(boolean updateImmediately) {
|
||||
if (isInShadowPass()) {
|
||||
return;
|
||||
}
|
||||
this.sectionCache.cleanup();
|
||||
this.flushBiomeRebuilds();
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_GRAPH_SEARCH);
|
||||
@@ -276,14 +256,19 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSubmitShadowGraphSearch() {
|
||||
return super.canSubmitShadowGraphSearch() && this.shadowRenderListManager.isNeedsUpdate()
|
||||
&& !this.shadowRenderListManager.hasOcclusionFutureInFlight();
|
||||
protected boolean canSubmitShadowGraphSearch() {
|
||||
return super.canSubmitShadowGraphSearch() && this.shadowRenderListManager.isNeedsUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean submitShadowGraphSearch(Viewport viewport, int frame) {
|
||||
return super.submitShadowGraphSearch(viewport, frame);
|
||||
protected void submitShadowGraphSearch(Viewport viewport, int frame) {
|
||||
super.submitShadowGraphSearch(viewport, frame);
|
||||
this.shadowSearchSubmissions++;
|
||||
}
|
||||
|
||||
/** Running count of submitted shadow searches; a change across a shadow pass means new lists are pending. */
|
||||
public int getShadowSearchSubmissions() {
|
||||
return this.shadowSearchSubmissions;
|
||||
}
|
||||
|
||||
public boolean isShadowGraphDirty() {
|
||||
@@ -314,20 +299,6 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderLayer(ChunkRenderMatrices matrices, TerrainRenderPass pass, CameraTransform occlusionCamera, CameraTransform camera) {
|
||||
// Shadow pass graph update is async - must wait for it to complete before rendering
|
||||
if (IrisShaderProviderHolder.isShadowPass()) {
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_SHADOW_GRAPH_WAIT);
|
||||
try {
|
||||
finishAllGraphUpdates();
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
}
|
||||
super.renderLayer(matrices, pass, occlusionCamera, camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process both asyncSubmittedTasks and AngelicaRenderQueue.TASKS.
|
||||
*/
|
||||
|
||||
+20
-11
@@ -35,6 +35,15 @@ public class CeleritasDebugScreenHandler {
|
||||
private List<String> cachedRight;
|
||||
private long lastBuildNanos;
|
||||
|
||||
static boolean isVerbose() {
|
||||
return AngelicaConfig.verboseF3 || SystemProperties.DEBUG_F3_DETAIL;
|
||||
}
|
||||
|
||||
/** Drops the cached right-hand lines so a verbose toggle shows on the next frame. */
|
||||
public static void invalidate() {
|
||||
INSTANCE.cachedRight = null;
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
public void onRenderGameOverlayTextEvent(RenderGameOverlayEvent.Text event) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
@@ -69,17 +78,17 @@ public class CeleritasDebugScreenHandler {
|
||||
lines.add("");
|
||||
lines.add(EnumChatFormatting.GREEN + "Angelica " + Tags.VERSION + " [Celeritas Renderer] [" + RENDER_BACKEND.getName() + "]");
|
||||
lines.addAll(renderer.getDebugStrings());
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm != null) {
|
||||
lines.add(sm.getDebugInfo());
|
||||
}
|
||||
lines.add(TessellatorStreamingDrawer.getDebugInfo());
|
||||
lines.add(TesrBatchRenderer.INSTANCE.getDebugSummaryLine());
|
||||
final String transferInfo = RENDER_BACKEND.getTransferDebugInfo();
|
||||
if (transferInfo != null) {
|
||||
lines.add(transferInfo);
|
||||
}
|
||||
if (SystemProperties.DEBUG_F3_DETAIL) {
|
||||
if (isVerbose()) {
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm != null) {
|
||||
lines.add(sm.getDebugInfo());
|
||||
}
|
||||
lines.add(TessellatorStreamingDrawer.getDebugInfo());
|
||||
lines.add(TesrBatchRenderer.INSTANCE.getDebugSummaryLine());
|
||||
final String transferInfo = RENDER_BACKEND.getTransferDebugInfo();
|
||||
if (transferInfo != null) {
|
||||
lines.add(transferInfo);
|
||||
}
|
||||
lines.addAll(TesrBatchRenderer.INSTANCE.getDebugDetailStrings());
|
||||
lines.add(ModelPartBatcher.INSTANCE.getDebugString());
|
||||
if (Tracy.ENABLED) {
|
||||
|
||||
@@ -2,17 +2,13 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.event.ChunkBiomeDataChangedEvent;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.embeddedt.embeddium.impl.gl.device.GLRenderDevice;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
public class CeleritasSetup {
|
||||
private static boolean initialized = false;
|
||||
|
||||
public static void ensureInitialized() {
|
||||
if (!initialized) {
|
||||
GLRenderDevice.VANILLA_STATE_RESETTER = () -> GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
ChunkBiomeDataChangedEvent.BUS.addListener(CeleritasSetup::onChunkBiomeDataChanged);
|
||||
AngelicaClientTweaker.LOGGER.debug("Celeritas init");
|
||||
initialized = true;
|
||||
|
||||
+91
-88
@@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.bop.FogBiomeCache;
|
||||
@@ -25,9 +24,8 @@ import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkRenderMatrices;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.data.MinecraftBuiltRenderSectionData;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.lists.ChunkRenderList;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.lists.SortedRenderLists;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTracker;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.map.ChunkTrackerHolder;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkMeshFormats;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
import org.embeddedt.embeddium.impl.render.terrain.SimpleWorldRenderer;
|
||||
@@ -41,6 +39,7 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.compat.cubicchunks.CubicChunksAPI;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
|
||||
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
@@ -78,7 +77,9 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
/** Debug flag for wireframe rendering mode. Toggle via /angelica wireframe */
|
||||
public static boolean DEBUG_WIREFRAME_MODE = false;
|
||||
|
||||
private final TileEntityRenderContext teRenderContext = new TileEntityRenderContext();
|
||||
private final TileEntityRenderContext teRenderContext = new TileEntityRenderContext(false);
|
||||
private final TileEntityRenderContext shadowCollectContext = new TileEntityRenderContext(true);
|
||||
private Viewport shadowPassPlayerViewport;
|
||||
private boolean useEntityCulling = true;
|
||||
|
||||
private FrustumIntersection teFrustum;
|
||||
@@ -86,9 +87,6 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
private int teCullEpoch;
|
||||
private double teCamX, teCamY, teCamZ;
|
||||
|
||||
// the volume of a section multiplied by the number of sections to be checked at most
|
||||
private static final double MAX_ENTITY_CHECK_VOLUME = 16 * 16 * 16 * 15;
|
||||
|
||||
private final ObjectArrayList<TileEntity> frameTEs = new ObjectArrayList<>();
|
||||
private final ObjectArrayList<TileEntity> frameOverride = new ObjectArrayList<>();
|
||||
|
||||
@@ -130,7 +128,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
public void runFrameIdleWork(long deadlineNanos) {
|
||||
final AngelicaRenderSectionManager rsm = this.renderSectionManager;
|
||||
if (rsm == null || this.world == null) return;
|
||||
if (rsm == null || this.world == null || rsm.isInShadowPass()) return;
|
||||
RenderDevice.enterManagedCode();
|
||||
try {
|
||||
long start = System.nanoTime();
|
||||
@@ -264,104 +262,105 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
return count;
|
||||
}
|
||||
|
||||
private CameraState lastMainCameraState;
|
||||
|
||||
@Override
|
||||
public void setupTerrain(Viewport viewport, CameraState cameraState, int frame, boolean spectator, boolean updateChunksImmediately) {
|
||||
var transform = viewport.getTransform();
|
||||
this.shadowPassPlayerViewport = null;
|
||||
|
||||
final CameraTransform transform = viewport.getTransform();
|
||||
if (transform.x == 0 && transform.y == 0 && transform.z == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderSectionManager.setCameraPosition(transform.x, transform.y, transform.z);
|
||||
|
||||
this.useEntityCulling = ClientProxy.options().performance.useEntityCulling;
|
||||
|
||||
if (renderSectionManager.isInShadowPass()) {
|
||||
if (lastMainCameraState != null) {
|
||||
cameraState = lastMainCameraState;
|
||||
}
|
||||
} else {
|
||||
lastMainCameraState = cameraState;
|
||||
// Same inputs as the shadow pass's state; reusing it keeps a fog-end change made while rendering shadows from
|
||||
// dirtying the graph a second time this frame.
|
||||
if (renderSectionManager.didShadowPassRunThisFrame() && this.lastCameraState != null) {
|
||||
cameraState = this.lastCameraState;
|
||||
}
|
||||
|
||||
super.setupTerrain(viewport, cameraState, frame, spectator, updateChunksImmediately);
|
||||
|
||||
if (!renderSectionManager.isInShadowPass()) {
|
||||
final GpuTerrainCuller terrainCuller = GpuTerrainCuller.activeInstance();
|
||||
if (terrainCuller != null) {
|
||||
terrainCuller.prepareAllPasses(createChunkRenderMatrices(), renderSectionManager.getRenderLists(),
|
||||
viewport.getTransform(), cameraTransform(cameraState.x(), cameraState.y(), cameraState.z()),
|
||||
IrisShaderProviderHolder.shouldUseFaceCulling());
|
||||
}
|
||||
final GpuTerrainCuller terrainCuller = GpuTerrainCuller.activeInstance();
|
||||
if (terrainCuller != null) {
|
||||
terrainCuller.prepareAllPasses(createChunkRenderMatrices(), renderSectionManager.getRenderLists(),
|
||||
viewport.getTransform(), cameraTransform(cameraState.x(), cameraState.y(), cameraState.z()),
|
||||
IrisShaderProviderHolder.shouldUseFaceCulling());
|
||||
}
|
||||
|
||||
// Process deferred dynamic light chunk rebuilds with frustum culling
|
||||
if (DynamicLights.isEnabled() && DynamicLights.FrustumCullingEnabled) {
|
||||
DynamicLights.get().processChunkRebuilds(viewport);
|
||||
}
|
||||
}
|
||||
|
||||
if (renderSectionManager.isInShadowPass()) {
|
||||
if (IrisShaderProviderHolder.isActive()) {
|
||||
collectTileEntitiesForShadow();
|
||||
}
|
||||
} else if (IrisShaderProviderHolder.isActive()) {
|
||||
IrisShaderProviderHolder.getProvider().preSubmitShadowGraph(frame);
|
||||
/** Captured before the shadow pass; the shadow pass's clipRenderersByFrustum only sees the shadow frustum. */
|
||||
public void setShadowPassPlayerViewport(Viewport viewport) {
|
||||
this.shadowPassPlayerViewport = viewport;
|
||||
}
|
||||
|
||||
/** Shadow-pass entry from clipRenderersByFrustum; falls back to the last terrain viewport if none was captured. */
|
||||
public void setupShadowTerrain(Viewport shadowViewport, CameraState cameraState, int frame, boolean spectator) {
|
||||
Viewport playerViewport = this.shadowPassPlayerViewport;
|
||||
this.shadowPassPlayerViewport = null;
|
||||
if (playerViewport == null) {
|
||||
playerViewport = this.getLastViewport();
|
||||
}
|
||||
if (playerViewport != null) {
|
||||
this.setupShadowTerrain(playerViewport, shadowViewport, cameraState, frame, spectator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupShadowTerrain(Viewport playerViewport, Viewport shadowViewport, CameraState cameraState, int frame, boolean spectator) {
|
||||
final CameraTransform transform = playerViewport.getTransform();
|
||||
if (transform.x == 0 && transform.y == 0 && transform.z == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.useEntityCulling = ClientProxy.options().performance.useEntityCulling;
|
||||
|
||||
super.setupShadowTerrain(playerViewport, shadowViewport, cameraState, frame, spectator);
|
||||
|
||||
if (DynamicLights.isEnabled() && DynamicLights.FrustumCullingEnabled) {
|
||||
DynamicLights.get().processChunkRebuilds(shadowViewport);
|
||||
}
|
||||
|
||||
if (IrisShaderProviderHolder.isActive()) {
|
||||
super.renderBlockEntities(this.shadowCollectContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processChunkEvents() {
|
||||
final ChunkTracker tracker = ChunkTrackerHolder.get(this.world);
|
||||
if (tracker instanceof CubeStatusTracker cubic) {
|
||||
cubic.forEachEvent(this.renderSectionManager);
|
||||
return;
|
||||
}
|
||||
syncChunkTrackingMode(tracker);
|
||||
super.processChunkEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initRenderer(CommandList commandList) {
|
||||
final ChunkTracker tracker = ChunkTrackerHolder.get(this.world);
|
||||
if (!(tracker instanceof CubeStatusTracker)) {
|
||||
syncChunkTrackingMode(tracker);
|
||||
}
|
||||
super.initRenderer(commandList);
|
||||
if (tracker instanceof CubeStatusTracker cubic) {
|
||||
cubic.forEachReady(this.renderSectionManager);
|
||||
}
|
||||
}
|
||||
|
||||
private static void syncChunkTrackingMode(ChunkTracker tracker) {
|
||||
tracker.setRequiredNeighborRadius(AngelicaConfig.useVanillaChunkTracking ? 0 : 1);
|
||||
}
|
||||
|
||||
public void setCurrentViewport(Viewport viewport) {
|
||||
this.currentViewport = viewport;
|
||||
}
|
||||
|
||||
public Viewport getCurrentViewport() {
|
||||
return this.currentViewport;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void collectTileEntitiesForShadow() {
|
||||
final SortedRenderLists renderLists = renderSectionManager.getRenderLists();
|
||||
final Iterator<ChunkRenderList> renderListIterator = renderLists.iterator();
|
||||
|
||||
while (renderListIterator.hasNext()) {
|
||||
final var renderList = renderListIterator.next();
|
||||
final var renderRegion = renderList.getRegion();
|
||||
final var renderSectionIterator = renderList.sectionsWithEntitiesIterator();
|
||||
|
||||
if (renderSectionIterator == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (renderSectionIterator.hasNext()) {
|
||||
final var renderSectionId = renderSectionIterator.nextByteAsInt();
|
||||
final var renderSection = renderRegion.getSection(renderSectionId);
|
||||
|
||||
if (renderSection == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final var context = renderSection.getBuiltContext();
|
||||
if (context instanceof MinecraftBuiltRenderSectionData<?, ?> mcData) {
|
||||
final var culledEntities = (List<TileEntity>) mcData.culledBlockEntities;
|
||||
if (!culledEntities.isEmpty()) {
|
||||
ShadowRenderer.visibleTileEntities.add(culledEntities);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var renderSection : renderSectionManager.getSectionsWithGlobalEntities()) {
|
||||
final var context = renderSection.getBuiltContext();
|
||||
if (context instanceof MinecraftBuiltRenderSectionData<?, ?> mcData) {
|
||||
final var globalEntities = (List<TileEntity>) mcData.globalBlockEntities;
|
||||
if (!globalEntities.isEmpty()) {
|
||||
ShadowRenderer.globalTileEntities.add(globalEntities);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CameraTransform cachedCameraTransform;
|
||||
private double cachedCamX, cachedCamY, cachedCamZ;
|
||||
|
||||
@@ -481,6 +480,11 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
|
||||
@Override
|
||||
protected int renderSectionBlockEntities(RenderSection section, List<TileEntity> blockEntities, boolean culled, TileEntityRenderContext renderContext) {
|
||||
if (renderContext.shadowCollect) {
|
||||
(culled ? ShadowRenderer.visibleTileEntities : ShadowRenderer.globalTileEntities).add(blockEntities);
|
||||
return blockEntities.size();
|
||||
}
|
||||
|
||||
if (culled) {
|
||||
if (section.getBuiltContext() instanceof AngelicaBuiltRenderSectionData angelicaData) {
|
||||
if (TeDistanceMath.distSqToSection(teCamX, teCamY, teCamZ,
|
||||
@@ -625,20 +629,19 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
return true;
|
||||
}
|
||||
|
||||
// bail on very large entities to avoid checking many sections
|
||||
final double entityVolume = (box.maxX - box.minX) * (box.maxY - box.minY) * (box.maxZ - box.minZ);
|
||||
if (entityVolume > MAX_ENTITY_CHECK_VOLUME) {
|
||||
// TODO: do a frustum check instead, even large entities aren't visible if they're outside the frustum
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isBoxVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ);
|
||||
}
|
||||
|
||||
public static class TileEntityRenderContext {
|
||||
/** Collects shadow-pass block entity lists for ShadowRenderer instead of rendering. */
|
||||
final boolean shadowCollect;
|
||||
public float partialTicks;
|
||||
public int pass;
|
||||
|
||||
TileEntityRenderContext(boolean shadowCollect) {
|
||||
this.shadowCollect = shadowCollect;
|
||||
}
|
||||
|
||||
public TileEntityRenderContext set(float partialTicks, int pass) {
|
||||
this.partialTicks = partialTicks;
|
||||
this.pass = pass;
|
||||
|
||||
@@ -7,7 +7,8 @@ import com.cardinalstar.cubicchunks.util.HashMap3D;
|
||||
import com.cardinalstar.cubicchunks.util.HashSet3D;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
public class CubeStatusTracker implements ChunkTracker {
|
||||
/** Tracks 3D cubes instead of columns; the inherited 2D state stays empty. */
|
||||
public class CubeStatusTracker extends ChunkTracker {
|
||||
|
||||
/// Cubes that have been synced to the client and are currently loaded.
|
||||
private final HashSet3D loadedCubes = new HashSet3D();
|
||||
@@ -28,14 +29,12 @@ public class CubeStatusTracker implements ChunkTracker {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFastMode(boolean fastMode) {
|
||||
this.fastMode = fastMode;
|
||||
|
||||
this.loadedCubes.forEach(this::pollCube);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFastModeEnabled() {
|
||||
return fastMode;
|
||||
}
|
||||
@@ -107,7 +106,6 @@ public class CubeStatusTracker implements ChunkTracker {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachReady(RenderSectionManager renderSectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
@@ -134,7 +132,6 @@ public class CubeStatusTracker implements ChunkTracker {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachEvent(RenderSectionManager renderSectionManager) {
|
||||
if (this.isFastModeEnabled() != AngelicaConfig.useVanillaChunkTracking) {
|
||||
setFastMode(AngelicaConfig.useVanillaChunkTracking);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
/** Decides when the section-age uniform upload can be skipped */
|
||||
public final class SectionAgeMath {
|
||||
|
||||
public static final long STEP_NANOS = 100_000_000L;
|
||||
|
||||
private SectionAgeMath() {}
|
||||
|
||||
public static long quantize(long timestampNanos) {
|
||||
return timestampNanos - Math.floorMod(timestampNanos, STEP_NANOS);
|
||||
}
|
||||
|
||||
public static long[] newState() {
|
||||
return new long[] { Long.MIN_VALUE, Long.MIN_VALUE, 0L };
|
||||
}
|
||||
|
||||
public static boolean canSkip(long[] state, long newestLoadTime, long quantizedTimestamp) {
|
||||
return state[0] == newestLoadTime && (state[2] == 1L || state[1] == quantizedTimestamp);
|
||||
}
|
||||
|
||||
public static void record(long[] state, long newestLoadTime, long quantizedTimestamp, boolean allSaturated) {
|
||||
state[0] = newestLoadTime;
|
||||
state[1] = quantizedTimestamp;
|
||||
state[2] = allSaturated ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,11 @@ public final class TerrainDrawStats {
|
||||
private static int cullMaxRegionCommands;
|
||||
private static long sectionMetaBytes;
|
||||
|
||||
// Running totals
|
||||
private static long indexBufferGrowths;
|
||||
private static long sectionsUploaded;
|
||||
private static long bytesUploaded;
|
||||
|
||||
private TerrainDrawStats() {
|
||||
}
|
||||
|
||||
@@ -44,6 +49,27 @@ public final class TerrainDrawStats {
|
||||
sectionMetaBytes += bytes;
|
||||
}
|
||||
|
||||
public static void recordIndexBufferGrowth() {
|
||||
indexBufferGrowths++;
|
||||
}
|
||||
|
||||
public static void recordSectionUpload(long bytes) {
|
||||
sectionsUploaded++;
|
||||
bytesUploaded += bytes;
|
||||
}
|
||||
|
||||
public static long getIndexBufferGrowths() {
|
||||
return indexBufferGrowths;
|
||||
}
|
||||
|
||||
public static long getSectionsUploaded() {
|
||||
return sectionsUploaded;
|
||||
}
|
||||
|
||||
public static long getBytesUploaded() {
|
||||
return bytesUploaded;
|
||||
}
|
||||
|
||||
public static int takeBatchRebuilds() {
|
||||
final int n = batchRebuilds;
|
||||
batchRebuilds = 0;
|
||||
|
||||
-2
@@ -27,6 +27,4 @@ public interface IrisShaderProvider {
|
||||
|
||||
/** Block render layer overrides from shader pack */
|
||||
@Nullable Map<Block, BlockRenderLayer> getBlockTypeIds();
|
||||
|
||||
default void preSubmitShadowGraph(int frame) {}
|
||||
}
|
||||
@@ -442,8 +442,9 @@ public final class GpuTerrainCuller {
|
||||
}
|
||||
|
||||
private void walkSortedGroup(ChunkRenderMatrices matrices, ChunkRenderListIterable renderLists, CameraTransform occlusionCamera, CameraTransform camera, boolean useBlockFaceCulling, boolean chained) {
|
||||
final int indexPointerMask = AngelicaRenderPassConfiguration.TRANSLUCENT_PASS.isSorted() ? 0xFFFFFFFF : 0;
|
||||
if (chained) {
|
||||
startSortedPass(0xFFFFFFFF);
|
||||
startSortedPass(indexPointerMask);
|
||||
} else {
|
||||
invalidatePreparedPasses();
|
||||
computeActiveThisPass = GpuCulling.mode().computeEnabled();
|
||||
@@ -452,7 +453,7 @@ public final class GpuTerrainCuller {
|
||||
passOutputBase = 0;
|
||||
walkPrimitiveType = null;
|
||||
primaryPass.reset(0, 0);
|
||||
sortedPass.reset(0xFFFFFFFF, 0);
|
||||
sortedPass.reset(indexPointerMask, 0);
|
||||
buildPass = sortedPass;
|
||||
current = sortedPass;
|
||||
}
|
||||
|
||||
@@ -766,6 +766,12 @@ public class SodiumGameOptionPages {
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.disableF3Additions = value, opts -> AngelicaConfig.disableF3Additions)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.verbosef3"))
|
||||
.setTooltip(I18n.format("options.angelica.verbosef3.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.verboseF3 = value, opts -> AngelicaConfig.verboseF3)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
if (AngelicaConfig.enableZoom) {
|
||||
|
||||
@@ -66,12 +66,6 @@ public class IrisCeleritasShaderProvider implements IrisShaderProvider {
|
||||
return BlockRenderingSettings.INSTANCE.getBlockTypeIds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSubmitShadowGraph(int frame) {
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline != null) pipeline.preSubmitShadowGraph(frame);
|
||||
}
|
||||
|
||||
public void deleteShaders() {
|
||||
overrides.deleteShaders();
|
||||
}
|
||||
|
||||
@@ -1958,11 +1958,6 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
|
||||
isRenderingFullScreenPass = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preSubmitShadowGraph(int frame) {
|
||||
if (shadowRenderer != null) shadowRenderer.preSubmitGraphUpdate(frame);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderShadows(EntityRenderer levelRenderer, Camera playerCamera) {
|
||||
if (shouldRenderPrepareBeforeShadow) {
|
||||
|
||||
@@ -81,6 +81,7 @@ public class ShadowRenderer {
|
||||
private static final Tracy.ZoneId Z_SHADOW_FRUSTUM = Tracy.zoneId("shadowFrustum", Tracy.COLOR_IRIS);
|
||||
private static final Tracy.ZoneId Z_SHADOW_VIEWPORT = Tracy.zoneId("shadowViewport", Tracy.COLOR_IRIS);
|
||||
private static final Tracy.ZoneId Z_SHADOW_MODEL_PARTS = Tracy.zoneId("shadowModelParts", Tracy.COLOR_IRIS);
|
||||
private static final Tracy.ZoneId Z_SHADOW_GRAPH_JOIN = Tracy.zoneId("shadowGraphJoin", Tracy.COLOR_IRIS);
|
||||
|
||||
public static final Matrix4f MODELVIEW = new Matrix4f();
|
||||
public static final FloatBuffer MODELVIEW_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
@@ -114,6 +115,7 @@ public class ShadowRenderer {
|
||||
private final boolean shouldRenderEntities;
|
||||
private final boolean shouldRenderPlayer;
|
||||
private final boolean shouldRenderBlockEntities;
|
||||
private final boolean shouldRenderLightBlockEntities;
|
||||
private final float sunPathRotation;
|
||||
private final List<MipmapPass> mipmapPasses = new ArrayList<>();
|
||||
private final String debugStringOverall;
|
||||
@@ -171,10 +173,9 @@ public class ShadowRenderer {
|
||||
private final FrustumCaches entityFrustumCaches = new FrustumCaches();
|
||||
private float lastGraphShadowAngle = Float.NaN;
|
||||
private float relayShadowAngle = Float.NaN;
|
||||
private FrustumHolder preSubmitFrustumHolder = new FrustumHolder("preSubmit");
|
||||
private final FrustumCaches preSubmitFrustumCaches = new FrustumCaches();
|
||||
private boolean preSubmitActive;
|
||||
private float preSubmittedShadowAngle = Float.NaN;
|
||||
/** A shadow search was submitted by the last shadow pass; with async occlusion its lists are published by this pass's join. */
|
||||
private boolean shadowSearchPending;
|
||||
private float searchedShadowAngle = Float.NaN;
|
||||
|
||||
private float shadowAngleDelta() {
|
||||
final Vector4f light = celestialUniforms.getShadowLightPositionInWorldSpace();
|
||||
@@ -185,25 +186,6 @@ public class ShadowRenderer {
|
||||
AngelicaConfig.shadowGraphHorizonScale);
|
||||
}
|
||||
|
||||
public void preSubmitGraphUpdate(int frame) {
|
||||
final AngelicaRenderSectionManager rsm = CeleritasWorldRenderer.getInstance().getRenderSectionManager();
|
||||
final float currentShadowAngle = getShadowAngle();
|
||||
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
|
||||
rsm.markShadowGraphDirty();
|
||||
lastGraphShadowAngle = currentShadowAngle;
|
||||
}
|
||||
if (!rsm.canSubmitShadowGraphSearch()) return;
|
||||
|
||||
preSubmitFrustumHolder = createShadowFrustum(renderDistanceMultiplier, preSubmitFrustumHolder, preSubmitFrustumCaches);
|
||||
if (!(preSubmitFrustumHolder.getFrustum() instanceof ViewportProvider provider)) return;
|
||||
final Vector3d entityPos = Camera.INSTANCE.getEntityPos();
|
||||
preSubmitFrustumHolder.getFrustum().setPosition(entityPos.x, entityPos.y, entityPos.z);
|
||||
|
||||
if (rsm.submitShadowGraphSearch(provider.sodium$createViewport(), frame)) {
|
||||
preSubmittedShadowAngle = lastGraphShadowAngle;
|
||||
preSubmitActive = true;
|
||||
}
|
||||
}
|
||||
private long lastRelayNanos;
|
||||
private final Vector3f shadowLightVectorCache = new Vector3f();
|
||||
private final MemoizedBoxCuller tileEntityCuller = new MemoizedBoxCuller();
|
||||
@@ -232,6 +214,7 @@ public class ShadowRenderer {
|
||||
this.shouldRenderEntities = shadowDirectives.shouldRenderEntities();
|
||||
this.shouldRenderPlayer = shadowDirectives.shouldRenderPlayer();
|
||||
this.shouldRenderBlockEntities = shadowDirectives.shouldRenderBlockEntities();
|
||||
this.shouldRenderLightBlockEntities = shadowDirectives.shouldRenderLightBlockEntities();
|
||||
this.shouldRenderDH = shadowDirectives.isDhShadowEnabled().orElse(false);
|
||||
this.packUsesShadowtex1 = packUsesShadowtex1;
|
||||
|
||||
@@ -797,7 +780,11 @@ public class ShadowRenderer {
|
||||
);
|
||||
}
|
||||
|
||||
private void renderTileEntities(Object bufferSource, MatrixStack modelView, double cameraX, double cameraY, double cameraZ, float partialTicks, boolean hasEntityFrustum) {
|
||||
private static boolean emitsLight(TileEntity tile) {
|
||||
return tile.getBlockType().getLightValue(tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord) > 0;
|
||||
}
|
||||
|
||||
private void renderTileEntities(Object bufferSource, MatrixStack modelView, double cameraX, double cameraY, double cameraZ, float partialTicks, boolean hasEntityFrustum, boolean lightsOnly) {
|
||||
profiler.startSection("iris_shadow_build_blockentities");
|
||||
|
||||
int shadowTileEntities = 0;
|
||||
@@ -826,6 +813,9 @@ public class ShadowRenderer {
|
||||
if (hasEntityFrustum && (culler.isCulled(tileEntity.xCoord - 1, tileEntity.yCoord - 1, tileEntity.zCoord - 1, tileEntity.xCoord + 1, tileEntity.yCoord + 1, tileEntity.zCoord + 1))) {
|
||||
continue;
|
||||
}
|
||||
if (lightsOnly && !emitsLight(tileEntity)) {
|
||||
continue;
|
||||
}
|
||||
renderTileEntity(tileEntity, cameraX, cameraY, cameraZ, partialTicks);
|
||||
|
||||
shadowTileEntities++;
|
||||
@@ -838,6 +828,9 @@ public class ShadowRenderer {
|
||||
if (hasEntityFrustum && (culler.isCulled(tileEntity.xCoord - 1, tileEntity.yCoord - 1, tileEntity.zCoord - 1, tileEntity.xCoord + 1, tileEntity.yCoord + 1, tileEntity.zCoord + 1))) {
|
||||
continue;
|
||||
}
|
||||
if (lightsOnly && !emitsLight(tileEntity)) {
|
||||
continue;
|
||||
}
|
||||
renderTileEntity(tileEntity, cameraX, cameraY, cameraZ, partialTicks);
|
||||
|
||||
shadowTileEntities++;
|
||||
@@ -864,8 +857,20 @@ public class ShadowRenderer {
|
||||
this.profiler = Minecraft.getMinecraft().mcProfiler;
|
||||
|
||||
profiler.endStartSection("iris_shadows");
|
||||
|
||||
final AngelicaRenderSectionManager rsm = CeleritasWorldRenderer.getInstance().getRenderSectionManager();
|
||||
// The in-flight shadow search reads terrainFrustumHolder's frustum, which createShadowFrustum re-inits below.
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_SHADOW_GRAPH_JOIN);
|
||||
try {
|
||||
rsm.finishAllGraphUpdates();
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
}
|
||||
|
||||
ACTIVE = true;
|
||||
CURRENT_TARGETS = this.targets;
|
||||
// ACTIVE routes clipRenderersByFrustum into the shadow path; a leak would send every main pass there.
|
||||
try {
|
||||
|
||||
// NB: We store the previous player buffers in order to be able to allow mods rendering entities in the shadow pass (Flywheel) to use the shadow buffers instead.
|
||||
// TODO: Render
|
||||
@@ -875,25 +880,27 @@ public class ShadowRenderer {
|
||||
visibleTileEntities.clear();
|
||||
globalTileEntities.clear();
|
||||
|
||||
final boolean newShadowLists = shadowSearchPending;
|
||||
shadowSearchPending = false;
|
||||
|
||||
final float currentShadowAngle = getShadowAngle();
|
||||
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
|
||||
CeleritasWorldRenderer.getInstance().getRenderSectionManager().markShadowGraphDirty();
|
||||
rsm.markShadowGraphDirty();
|
||||
lastGraphShadowAngle = currentShadowAngle;
|
||||
}
|
||||
|
||||
final boolean deferActive = shouldRenderTerrain;
|
||||
final long frameNanos = System.nanoTime();
|
||||
final boolean consumePreSubmit = preSubmitActive;
|
||||
preSubmitActive = false;
|
||||
final boolean shadowGraphDirty = rsm.isShadowGraphDirty();
|
||||
|
||||
final boolean relayTerrain = shadowTerrainAlwaysRelays()
|
||||
|| !targets.isTerrainSnapshotValid()
|
||||
|| consumePreSubmit
|
||||
|| CeleritasWorldRenderer.getInstance().getRenderSectionManager().isShadowGraphDirty()
|
||||
|| newShadowLists
|
||||
|| shadowGraphDirty
|
||||
|| frameNanos - lastRelayNanos >= SHADOW_RELAY_MAX_AGE_NANOS;
|
||||
if (relayTerrain) {
|
||||
|
||||
relayShadowAngle = consumePreSubmit && !CeleritasWorldRenderer.getInstance().getRenderSectionManager().isShadowGraphDirty() ? preSubmittedShadowAngle : currentShadowAngle;
|
||||
// New lists draw at the angle they were searched at, unless the gate already wants a newer search.
|
||||
relayShadowAngle = newShadowLists && !shadowGraphDirty ? searchedShadowAngle : currentShadowAngle;
|
||||
}
|
||||
activeShadowAngle = relayShadowAngle;
|
||||
SHADOW_TERRAIN_RELAID = relayTerrain;
|
||||
@@ -934,11 +941,9 @@ public class ShadowRenderer {
|
||||
|
||||
profiler.endSection();
|
||||
|
||||
// Save the main camera viewport before shadow pass overwrites it.
|
||||
// clipRenderersByFrustum -> setupTerrain sets currentViewport to the shadow frustum.
|
||||
// If the shadow pass throws or the main setupTerrain doesn't run after us, the shadow
|
||||
// viewport would persist and corrupt entity culling in the main pass.
|
||||
final Viewport savedViewport = CeleritasWorldRenderer.getInstance().getCurrentViewport();
|
||||
// clipRenderersByFrustum -> setupShadowTerrain sets currentViewport to the shadow frustum; restore the last
|
||||
// terrain viewport so entity culling never sees the shadow one if the main setupTerrain doesn't run.
|
||||
final Viewport savedViewport = CeleritasWorldRenderer.getInstance().getLastViewport();
|
||||
|
||||
// Pair setupGlState / restoreGlState with try-finally so any throw between them
|
||||
// (translucent terrain, entity rendering, mipmaps, etc.) still unwinds the
|
||||
@@ -947,7 +952,13 @@ public class ShadowRenderer {
|
||||
boolean setupGlStateRan = false;
|
||||
try {
|
||||
// Execute the vanilla terrain setup / culling routines using our shadow frustum.
|
||||
final int shadowSearchesBefore = rsm.getShadowSearchSubmissions();
|
||||
mc.renderGlobal.clipRenderersByFrustum(terrainFrustumHolder.getFrustum(), playerCamera.getPartialTicks());
|
||||
if (rsm.getShadowSearchSubmissions() != shadowSearchesBefore) {
|
||||
shadowSearchPending = true;
|
||||
// createShadowFrustum built the search's light vector from the current light
|
||||
searchedShadowAngle = currentShadowAngle;
|
||||
}
|
||||
|
||||
// Don't forget to increment the frame counter! This variable is arbitrary and only used in terrain setup,
|
||||
// and if it's not incremented, the vanilla culling code will get confused and think that it's already seen
|
||||
@@ -983,9 +994,9 @@ public class ShadowRenderer {
|
||||
renderShadowEntitiesAndPlayer(levelRenderer, entityShadowFrustum, modelView, entityX, entityY, entityZ, tickDelta);
|
||||
}
|
||||
|
||||
if (shouldRenderBlockEntities) {
|
||||
if (shouldRenderBlockEntities || shouldRenderLightBlockEntities) {
|
||||
try (GLDebug.Scope s = GLDebug.scope("shadow:block_entities")) {
|
||||
renderTileEntities(null, modelView, entityX, entityY, entityZ, tickDelta, entityFrustumConstrained);
|
||||
renderTileEntities(null, modelView, entityX, entityY, entityZ, tickDelta, entityFrustumConstrained, !shouldRenderBlockEntities);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,9 +1047,10 @@ public class ShadowRenderer {
|
||||
profiler.endStartSection("iris_shadowcomp");
|
||||
|
||||
if (compositeRenderer != null) compositeRenderer.renderAll();
|
||||
|
||||
ACTIVE = false;
|
||||
CURRENT_TARGETS = null;
|
||||
} finally {
|
||||
ACTIVE = false;
|
||||
CURRENT_TARGETS = null;
|
||||
}
|
||||
profiler.endSection();
|
||||
profiler.endStartSection("culling");
|
||||
}
|
||||
@@ -1062,7 +1074,7 @@ public class ShadowRenderer {
|
||||
}
|
||||
|
||||
private String getTileEntitiesDebugString() {
|
||||
return shouldRenderBlockEntities ? (renderedShadowTileEntities + "/" + Minecraft.getMinecraft().theWorld.loadedTileEntityList.size()) : "disabled by pack";
|
||||
return (shouldRenderBlockEntities || shouldRenderLightBlockEntities) ? (renderedShadowTileEntities + "/" + Minecraft.getMinecraft().theWorld.loadedTileEntityList.size()) : "disabled by pack";
|
||||
}
|
||||
|
||||
private static class MipmapPass {
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.OptionalInt;
|
||||
public interface WorldRenderingPipeline {
|
||||
void beginLevelRendering();
|
||||
void renderShadows(EntityRenderer levelRenderer, Camera camera);
|
||||
default void preSubmitShadowGraph(int frame) {}
|
||||
void addDebugText(List<String> messages);
|
||||
OptionalInt getForcedShadowRenderDistanceChunksForDisplay();
|
||||
|
||||
|
||||
@@ -611,16 +611,6 @@ public final class RwImageStoreExtractor {
|
||||
final Set<String> present = new HashSet<>();
|
||||
for (AttributeSlot slot : attrs) present.add(slot.name());
|
||||
|
||||
out.append("#ifdef USE_VERTEX_COMPRESSION\n");
|
||||
if (present.contains("a_PosId")) {
|
||||
out.append(" _vert_position = vec3(a_PosId.xyz) * VERT_POS_SCALE + VERT_POS_OFFSET;\n");
|
||||
out.append(" _draw_id = (a_PosId.w >> 8u) & 0xFFu;\n");
|
||||
out.append(" _material_params = (a_PosId.w >> 0u) & 0xFFu;\n");
|
||||
}
|
||||
if (present.contains("a_TexCoord")) out.append(" _vert_tex_diffuse_coord = a_TexCoord * VERT_TEX_SCALE;\n");
|
||||
if (present.contains("a_LightCoord")) out.append(" _vert_tex_light_coord = a_LightCoord;\n");
|
||||
if (present.contains("a_Color")) out.append(" _vert_color = a_Color;\n");
|
||||
out.append("#else\n");
|
||||
if (present.contains("a_PosId")) out.append(" _vert_position = a_PosId;\n");
|
||||
if (present.contains("a_TexCoord")) out.append(" _vert_tex_diffuse_coord = a_TexCoord;\n");
|
||||
if (present.contains("a_Color")) out.append(" _vert_color = a_Color;\n");
|
||||
@@ -630,7 +620,6 @@ public final class RwImageStoreExtractor {
|
||||
out.append(" _draw_id = (_vg_draw_params >> 8) & 0xFFu;\n");
|
||||
out.append(" _vert_tex_light_coord = ivec2((uvec2((a_LightCoord >> 16) & 0xFFFFu) >> uvec2(0, 8)) & uvec2(0xFFu));\n");
|
||||
}
|
||||
out.append("#endif\n");
|
||||
}
|
||||
|
||||
private static void emitDispatchMain(StringBuilder out, RwExtractMode mode, List<AttributeSlot> attrs) {
|
||||
@@ -788,11 +777,6 @@ public final class RwImageStoreExtractor {
|
||||
addAttr(m, new AttributeSlot("a_LightCoord", "uint", 24,
|
||||
"_vg_vbuf.data[base + 6u]"));
|
||||
|
||||
addAttr(m, new AttributeSlot("a_PosId", "uvec4", 0,
|
||||
"uvec4(0u, 0u, 0u, 0u)", false));
|
||||
addAttr(m, new AttributeSlot("a_LightCoord", "ivec2", 24,
|
||||
"ivec2(0, 0)", false));
|
||||
|
||||
final LinkedHashMap<String, Map<String, AttributeSlot>> immutable = new LinkedHashMap<>();
|
||||
for (var e : m.entrySet()) immutable.put(e.getKey(), Map.copyOf(e.getValue()));
|
||||
return Map.copyOf(immutable);
|
||||
|
||||
@@ -29,6 +29,7 @@ public class PackShadowDirectives {
|
||||
private final boolean shouldRenderEntities;
|
||||
private final boolean shouldRenderPlayer;
|
||||
private final boolean shouldRenderBlockEntities;
|
||||
private final boolean shouldRenderLightBlockEntities;
|
||||
@Getter private final ShadowCullState cullingState;
|
||||
private final OptionalBoolean dhShadowEnabled;
|
||||
private float nearPlane, farPlane;
|
||||
@@ -83,6 +84,7 @@ public class PackShadowDirectives {
|
||||
this.shouldRenderEntities = properties.getShadowEntities().orElse(true);
|
||||
this.shouldRenderPlayer = properties.getShadowPlayer().orElse(false);
|
||||
this.shouldRenderBlockEntities = properties.getShadowBlockEntities().orElse(true);
|
||||
this.shouldRenderLightBlockEntities = properties.getShadowLightBlockEntities().orElse(false);
|
||||
this.cullingState = properties.getShadowCulling();
|
||||
this.shadowEnabled = properties.getShadowEnabled();
|
||||
this.dhShadowEnabled = properties.getDhShadowEnabled();
|
||||
@@ -114,6 +116,7 @@ public class PackShadowDirectives {
|
||||
this.shouldRenderEntities = shadowDirectives.shouldRenderEntities;
|
||||
this.shouldRenderPlayer = shadowDirectives.shouldRenderPlayer;
|
||||
this.shouldRenderBlockEntities = shadowDirectives.shouldRenderBlockEntities;
|
||||
this.shouldRenderLightBlockEntities = shadowDirectives.shouldRenderLightBlockEntities;
|
||||
this.cullingState = shadowDirectives.cullingState;
|
||||
this.depthSamplingSettings = shadowDirectives.depthSamplingSettings;
|
||||
this.colorSamplingSettings = shadowDirectives.colorSamplingSettings;
|
||||
@@ -185,6 +188,10 @@ public class PackShadowDirectives {
|
||||
return shouldRenderBlockEntities;
|
||||
}
|
||||
|
||||
public boolean shouldRenderLightBlockEntities() {
|
||||
return shouldRenderLightBlockEntities;
|
||||
}
|
||||
|
||||
public OptionalBoolean isShadowEnabled() {
|
||||
return shadowEnabled;
|
||||
}
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
com.gtnewhorizons.angelica.client.rendering.AngelicaFogService
|
||||
@@ -83,7 +83,7 @@ sodium.options.use_particle_culling.tooltip=If enabled, only particles which are
|
||||
sodium.options.animate_only_visible_textures.name=Animate Only Visible Textures
|
||||
sodium.options.animate_only_visible_textures.tooltip=If enabled, only animated textures determined to be visible will be updated. This can provide a significant boost to frame rates on some hardware, especially with heavier resource packs. If you experience issues with some textures not being animated, try disabling this option.
|
||||
sodium.options.translucency_sorting.name=Translucency Sorting
|
||||
sodium.options.translucency_sorting.tooltip=If enabled, translucent blocks will be sorted correctly from back to front to provide better rendering, especially when multiple translucent blocks are in view and layered. This is a bit of an expensive operation.
|
||||
sodium.options.translucency_sorting.tooltip=If enabled, translucent terrain (water, stained glass, ice) and translucent tile entities are sorted back to front so overlapping layers draw correctly. Disabling improves performance in scenes with lots of translucent blocks but causes layering artifacts. Changing this reloads chunks.
|
||||
sodium.options.cpu_render_ahead_limit.name=CPU Render-Ahead Limit
|
||||
sodium.options.cpu_render_ahead_limit.tooltip=How many frames the CPU may run ahead of the GPU. Lower values cut input latency, higher values smooth out uneven frame times. Very low or high values may create frame rate instability. 0 disables it; the SDL-GPU backend ignores it and applies its own backpressure.
|
||||
sodium.options.cpu_render_ahead_limit.value=%s frame(s)
|
||||
@@ -301,6 +301,10 @@ options.angelica.aggressiveChunkLoading=Aggressive Chunk Loading
|
||||
options.angelica.aggressiveChunkLoading.tooltip=Renders chunks immediately when loaded, even if neighbors aren't ready yet. Improves perceived loading speed at render distance edges. Chunks rebuild automatically when neighbors load. Useful for low render distance servers.
|
||||
options.angelica.disablef3=Disable F3 Additions
|
||||
options.angelica.disablef3.tooltip=Disables Angelica's additions to the F3 debug screen.
|
||||
options.angelica.verbosef3=Verbose F3
|
||||
options.angelica.verbosef3.tooltip=Shows developer counters (fixed-function shaders, streaming, TESR batching, GPU transfer) on the F3 screen. Toggle in game with F3+V.
|
||||
angelica.debug.verbose.on=Verbose F3: shown
|
||||
angelica.debug.verbose.off=Verbose F3: hidden
|
||||
options.angelica.texteffects.rgb_colors=RGB Colors
|
||||
options.angelica.texteffects.rgb_colors.tooltip=Enable hex colors via %s syntax
|
||||
options.angelica.texteffects.gradients=Gradients
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
#version 330 core
|
||||
|
||||
#ifdef LEGACY
|
||||
#extension GL_EXT_gpu_shader4 : require
|
||||
#endif
|
||||
|
||||
#import <sodium:include/fog.glsl>
|
||||
|
||||
in vec4 v_Color;
|
||||
in vec4 v_RdhFactor;
|
||||
in vec2 v_QuadCoord;
|
||||
in vec2 v_TexCoord;
|
||||
|
||||
#if defined(USE_FOG) && defined(CHUNK_FADE_IN_DURATION_MS) && CHUNK_FADE_IN_DURATION_MS > 0
|
||||
in float v_ChunkAgeMs;
|
||||
#endif
|
||||
|
||||
in float v_MaterialMipBias;
|
||||
#ifdef USE_FRAGMENT_DISCARD
|
||||
@@ -19,13 +27,14 @@ in float v_CylindricalFragDistance;
|
||||
in float v_FragDistance;
|
||||
#endif
|
||||
|
||||
uniform sampler2D u_BlockTex;
|
||||
uniform sampler2D u_BlockTex; // The block texture
|
||||
|
||||
uniform vec4 u_FogColor;
|
||||
uniform int u_FogShape;
|
||||
uniform vec4 u_FogColor; // The color of the shader fog
|
||||
|
||||
#ifdef USE_FOG_SMOOTH
|
||||
uniform float u_FogStart;
|
||||
uniform float u_FogEnd;
|
||||
uniform float u_FogStart; // The starting position of the shader fog
|
||||
uniform float u_FogEnd; // The ending position of the shader fog
|
||||
#endif
|
||||
|
||||
#ifdef USE_FOG_POSTMODERN
|
||||
@@ -36,11 +45,11 @@ uniform float u_EnvFogEnd;
|
||||
#endif
|
||||
|
||||
#if defined(USE_FOG_EXP) || defined(USE_FOG_EXP2)
|
||||
uniform float u_FogDensity;
|
||||
uniform float u_FogDensity; // The density of the shader fog
|
||||
#endif
|
||||
|
||||
#ifndef LEGACY
|
||||
out vec4 fragColor;
|
||||
out vec4 fragColor; // The output fragment for the color framebuffer
|
||||
#else
|
||||
#define fragColor gl_FragColor
|
||||
#endif
|
||||
@@ -181,32 +190,51 @@ void main() {
|
||||
#endif
|
||||
|
||||
vec4 m_color = v_Color;
|
||||
// Apply correction factor to make the resulting color very close to what true bilinear interpolation would obtain
|
||||
// min(x, y) * (1 - max(x, y)) == min(x, y) - (x * y)
|
||||
float correctionWeight = min(v_QuadCoord.x, v_QuadCoord.y) - (v_QuadCoord.x * v_QuadCoord.y);
|
||||
m_color += v_RdhFactor * correctionWeight;
|
||||
|
||||
#ifdef USE_VANILLA_COLOR_FORMAT
|
||||
// Apply per-vertex color. AO shade is applied ahead of time on the CPU.
|
||||
diffuseColor *= m_color;
|
||||
#else
|
||||
// Apply per-vertex color
|
||||
diffuseColor.rgb *= m_color.rgb;
|
||||
|
||||
// Apply ambient occlusion "shade"
|
||||
diffuseColor.rgb *= m_color.a;
|
||||
#endif
|
||||
|
||||
#ifdef USE_FOG
|
||||
|
||||
#if defined(CHUNK_FADE_IN_DURATION_MS) && CHUNK_FADE_IN_DURATION_MS > 0
|
||||
// Make chunk fade in over a short duration
|
||||
diffuseColor = vec4(mix(u_FogColor.rgb, diffuseColor.rgb, (clamp(v_ChunkAgeMs, 0, CHUNK_FADE_IN_DURATION_MS) / CHUNK_FADE_IN_DURATION_MS)), diffuseColor.a);
|
||||
#endif
|
||||
|
||||
#ifdef USE_FOG_POSTMODERN
|
||||
#if defined(USE_FOG_POSTMODERN)
|
||||
float fogValue = max(_linearFogValue(v_CylindricalFragDistance, u_RenderDistFogStart, u_RenderDistFogEnd),
|
||||
_linearFogValue(v_SphericalFragDistance, u_EnvFogStart, u_EnvFogEnd));
|
||||
|
||||
fragColor = vec4(mix(diffuseColor.rgb, u_FogColor.rgb, fogValue * u_FogColor.a), diffuseColor.a);
|
||||
#elif defined(USE_FOG_EXP2)
|
||||
fragColor = _exp2Fog(diffuseColor, v_FragDistance, u_FogColor, u_FogDensity);
|
||||
#else // Legacy fog
|
||||
float fragDistance;
|
||||
if (u_FogShape == FOG_SHAPE_PLANAR) {
|
||||
fragDistance = gl_FragCoord.z / gl_FragCoord.w;
|
||||
} else {
|
||||
fragDistance = v_FragDistance;
|
||||
}
|
||||
#if defined(USE_FOG_EXP2)
|
||||
fragColor = _exp2Fog(diffuseColor, fragDistance, u_FogColor, u_FogDensity);
|
||||
#elif defined(USE_FOG_EXP)
|
||||
fragColor = _expFog(diffuseColor, v_FragDistance, u_FogColor, u_FogDensity);
|
||||
fragColor = _expFog(diffuseColor, fragDistance, u_FogColor, u_FogDensity);
|
||||
#elif defined(USE_FOG_SMOOTH)
|
||||
fragColor = _linearFog(diffuseColor, v_FragDistance, u_FogColor, u_FogStart, u_FogEnd);
|
||||
fragColor = _linearFog(diffuseColor, fragDistance, u_FogColor, u_FogStart, u_FogEnd);
|
||||
#endif
|
||||
#else
|
||||
#endif
|
||||
|
||||
#else // No fog
|
||||
fragColor = diffuseColor;
|
||||
#endif
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SectionAgeMath;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloatArray;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.DefaultChunkShaderInterface;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/** Skips section-age uniform uploads that cannot have changed; see {@link SectionAgeMath} */
|
||||
@Mixin(value = DefaultChunkShaderInterface.class, remap = false)
|
||||
public abstract class MixinDefaultChunkShaderInterface {
|
||||
|
||||
@Shadow @Final private GlUniformFloatArray uniformChunkAges;
|
||||
|
||||
/** Per-region {@link SectionAgeMath} skip state. */
|
||||
@Unique private final WeakHashMap<long[], long[]> angelica$ageState = new WeakHashMap<>();
|
||||
|
||||
@Unique private FloatBuffer angelica$ageScratch;
|
||||
|
||||
public void setSectionAges(long timestamp, long[] loadTimes, long newestLoadTime) {
|
||||
final int n = loadTimes.length;
|
||||
if (this.uniformChunkAges == null || n == 0) return;
|
||||
|
||||
final long tsQ = SectionAgeMath.quantize(timestamp);
|
||||
long[] state = angelica$ageState.get(loadTimes);
|
||||
if (state != null && SectionAgeMath.canSkip(state, newestLoadTime, tsQ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean allSaturated = angelica$buildAndUpload(tsQ, loadTimes, n);
|
||||
|
||||
if (state == null) {
|
||||
state = SectionAgeMath.newState();
|
||||
angelica$ageState.put(loadTimes, state);
|
||||
}
|
||||
SectionAgeMath.record(state, newestLoadTime, tsQ, allSaturated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Angelica
|
||||
* @reason reused direct buffer
|
||||
*/
|
||||
@Overwrite
|
||||
public void setSectionAges(long timestamp, long[] loadTimes) {
|
||||
final int n = loadTimes.length;
|
||||
if (this.uniformChunkAges == null || n == 0) return;
|
||||
angelica$buildAndUpload(timestamp, loadTimes, n);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean angelica$buildAndUpload(long timestamp, long[] loadTimes, int n) {
|
||||
FloatBuffer buf = angelica$ageScratch;
|
||||
if (buf == null || buf.capacity() < n) {
|
||||
buf = ByteBuffer.allocateDirect(n * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
angelica$ageScratch = buf;
|
||||
}
|
||||
long ptr = MemoryUtilities.memAddress(buf);
|
||||
boolean allSaturated = true;
|
||||
for (int i = 0; i < n; i++) {
|
||||
final long ageMs = (timestamp - loadTimes[i]) / 1_000_000L;
|
||||
final float v;
|
||||
if (ageMs >= 30000L) {
|
||||
v = 30000f;
|
||||
} else {
|
||||
v = (float) ageMs;
|
||||
allSaturated = false;
|
||||
}
|
||||
MemoryUtilities.memPutFloat(ptr, v);
|
||||
ptr += 4;
|
||||
}
|
||||
buf.position(0).limit(n);
|
||||
this.uniformChunkAges.set(buf);
|
||||
return allSaturated;
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.rendering.AngelicaFogService;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.Camera;
|
||||
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
|
||||
import com.gtnewhorizons.angelica.event.RenderChunkEvent;
|
||||
@@ -29,8 +30,8 @@ import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkShaderFogComponent;
|
||||
import org.embeddedt.embeddium.impl.render.terrain.SimpleWorldRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.ViewportProvider;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
@@ -200,7 +201,7 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
|
||||
try {
|
||||
final Entity viewEntity = this.mc.renderViewEntity;
|
||||
final float fogDistance = ChunkShaderFogComponent.FOG_SERVICE.getFogCutoff();
|
||||
final float fogDistance = AngelicaFogService.INSTANCE.getFogCutoff();
|
||||
|
||||
final double camX = lerp(viewEntity.lastTickPosX, viewEntity.posX, partialTicks);
|
||||
final double camY = lerp(viewEntity.lastTickPosY, viewEntity.posY, partialTicks) + viewEntity.getEyeHeight();
|
||||
@@ -222,7 +223,12 @@ public class MixinRenderGlobal implements IRenderGlobalExt {
|
||||
camX, camY, camZ,
|
||||
viewEntity.rotationPitch, viewEntity.rotationYaw, fogDistance
|
||||
);
|
||||
this.celeritas$renderer.setupTerrain(((ViewportProvider)camera).sodium$createViewport(), cameraState, this.celeritas$frame++, angelica$isSpectatorMode(), false);
|
||||
final Viewport viewport = ((ViewportProvider) camera).sodium$createViewport();
|
||||
if (this.celeritas$renderer.isInShadowPass()) {
|
||||
this.celeritas$renderer.setupShadowTerrain(viewport, cameraState, this.celeritas$frame++, angelica$isSpectatorMode());
|
||||
} else {
|
||||
this.celeritas$renderer.setupTerrain(viewport, cameraState, this.celeritas$frame++, angelica$isSpectatorMode(), false);
|
||||
}
|
||||
} finally {
|
||||
RenderDevice.exitManagedCode();
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.TerrainDrawStats;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkTaskOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.executor.ChunkJobResult;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.data.BuiltSectionMeshParts;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.region.RenderRegionManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Mixin(value = RenderRegionManager.class, remap = false)
|
||||
public abstract class MixinRenderRegionManager {
|
||||
|
||||
@Inject(method = "uploadMeshes", at = @At("HEAD"))
|
||||
private void angelica$onUploadMeshes(CommandList commandList, Collection<ChunkJobResult.Success<? extends ChunkTaskOutput>> results, Runnable graphUpdateTrigger, CallbackInfo ci) {
|
||||
for (var holder : results) {
|
||||
if (!(holder.output() instanceof ChunkBuildOutput buildOutput)) continue;
|
||||
for (BuiltSectionMeshParts mesh : buildOutput.meshes.values()) {
|
||||
long bytes = mesh.vertexBuffer().getLength();
|
||||
if (mesh.indexBuffer() != null) bytes += mesh.indexBuffer().getLength();
|
||||
TerrainDrawStats.recordSectionUpload(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,8 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.RenderSectionManagerAccessor;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkTaskOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.executor.ChunkJobResult;
|
||||
@@ -15,17 +13,10 @@ import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
@Mixin(value = RenderSectionManager.class, remap = false)
|
||||
public class MixinRenderSectionManager implements RenderSectionManagerAccessor {
|
||||
|
||||
@Shadow @Final private Long2ReferenceMap<RenderSection> sectionByPosition;
|
||||
|
||||
@Shadow @Final private ConcurrentLinkedDeque<Runnable> asyncSubmittedTasks;
|
||||
|
||||
@Shadow @Final private ConcurrentLinkedDeque<ChunkJobResult<? extends ChunkTaskOutput>> buildResults;
|
||||
|
||||
@Override
|
||||
public Long2ReferenceMap<RenderSection> angelica$getSectionByPosition() {
|
||||
return sectionByPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentLinkedDeque<Runnable> angelica$getAsyncSubmittedTasks() {
|
||||
return asyncSubmittedTasks;
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = ShaderLoader.class, remap = false)
|
||||
public class MixinShaderLoader {
|
||||
|
||||
/** The desktop GL 3.2 capability check can fail on ES 3.2; ES must never take the GLSL 1.20 path. */
|
||||
@Inject(method = "useLegacyGlsl", at = @At("HEAD"), cancellable = true)
|
||||
private static void angelica$noLegacyGlslOnGles(CallbackInfoReturnable<Boolean> cir) {
|
||||
if (RenderSystem.isGLES()) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.celeritas.terrain;
|
||||
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.TerrainDrawStats;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.SharedQuadIndexBuffer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = SharedQuadIndexBuffer.class, remap = false)
|
||||
public abstract class MixinSharedQuadIndexBuffer {
|
||||
|
||||
@Inject(method = "grow", at = @At("HEAD"))
|
||||
private void angelica$onGrow(CommandList commandList, int primitiveCount, CallbackInfo ci) {
|
||||
TerrainDrawStats.recordIndexBufferGrowth();
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -9,14 +9,13 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaChunkTracker;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CubeStatusTracker;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.SmoothBiomeColorCache;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.WorldClientExtension;
|
||||
|
||||
@Mixin(WorldClient.class)
|
||||
public class MixinWorldClient implements ChunkTrackerHolder, WorldClientExtension {
|
||||
@Unique private final ChunkTracker celeritas$tracker = ModStatus.isCubicChunksLoaded ? new CubeStatusTracker() : new AngelicaChunkTracker();
|
||||
@Unique private final ChunkTracker celeritas$tracker = ModStatus.isCubicChunksLoaded ? new CubeStatusTracker() : new ChunkTracker();
|
||||
@Unique private final SmoothBiomeColorCache celeritas$smoothBiomeColorCache = new SmoothBiomeColorCache((IBlockAccess) this);
|
||||
|
||||
@Override
|
||||
|
||||
+9
-1
@@ -2,8 +2,10 @@ package com.gtnewhorizons.angelica.mixins.early.shaders;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.mojang.Camera;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.llamalad7.mixinextras.sugar.Share;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import jss.notfine.core.SettingsManager;
|
||||
@@ -24,10 +26,12 @@ import net.minecraft.client.renderer.EntityRenderer;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.culling.Frustrum;
|
||||
import net.minecraft.client.resources.IResourceManagerReloadListener;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.ViewportProvider;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -85,7 +89,11 @@ public abstract class MixinEntityRenderer implements IResourceManagerReloadListe
|
||||
}
|
||||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderGlobal;clipRenderersByFrustum(Lnet/minecraft/client/renderer/culling/ICamera;F)V"), method = "renderWorld(FJ)V")
|
||||
private void iris$renderShadows(float partialTicks, long startTime, CallbackInfo ci, @Share("pipeline") LocalRef<WorldRenderingPipeline> pipeline) {
|
||||
private void iris$renderShadows(float partialTicks, long startTime, CallbackInfo ci, @Share("pipeline") LocalRef<WorldRenderingPipeline> pipeline, @Local Frustrum playerFrustum) {
|
||||
final CeleritasWorldRenderer renderer = CeleritasWorldRenderer.getInstanceOrNull();
|
||||
if (renderer != null) {
|
||||
renderer.setShadowPassPlayerViewport(((ViewportProvider) playerFrustum).sodium$createViewport());
|
||||
}
|
||||
pipeline.get().renderShadows((EntityRenderer) (Object) this, Camera.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static com.gtnewhorizons.angelica.rendering.celeritas.SectionAgeMath.STEP_NANOS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SectionAgeMathTest {
|
||||
|
||||
@Test
|
||||
void quantizeRoundsDownToStep() {
|
||||
assertEquals(0L, SectionAgeMath.quantize(0L));
|
||||
assertEquals(0L, SectionAgeMath.quantize(STEP_NANOS - 1));
|
||||
assertEquals(STEP_NANOS, SectionAgeMath.quantize(STEP_NANOS));
|
||||
assertEquals(7 * STEP_NANOS, SectionAgeMath.quantize(7 * STEP_NANOS + 42));
|
||||
|
||||
assertEquals(-STEP_NANOS, SectionAgeMath.quantize(-1L));
|
||||
assertEquals(-2 * STEP_NANOS, SectionAgeMath.quantize(-STEP_NANOS - 1));
|
||||
assertEquals(-STEP_NANOS, SectionAgeMath.quantize(-STEP_NANOS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void freshStateNeverSkips() {
|
||||
final long[] state = SectionAgeMath.newState();
|
||||
assertFalse(SectionAgeMath.canSkip(state, 0L, 0L));
|
||||
assertFalse(SectionAgeMath.canSkip(state, 123L, SectionAgeMath.quantize(456L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsaturatedSkipsOnlyWithinTheSameStep() {
|
||||
final long[] state = SectionAgeMath.newState();
|
||||
final long newest = 1_000L;
|
||||
final long tsQ = SectionAgeMath.quantize(50 * STEP_NANOS + 3);
|
||||
SectionAgeMath.record(state, newest, tsQ, false);
|
||||
|
||||
assertTrue(SectionAgeMath.canSkip(state, newest, tsQ));
|
||||
assertFalse(SectionAgeMath.canSkip(state, newest, tsQ + STEP_NANOS), "next step must rebuild");
|
||||
assertFalse(SectionAgeMath.canSkip(state, newest + 1, tsQ), "newer section load must rebuild");
|
||||
}
|
||||
|
||||
@Test
|
||||
void saturatedSkipsAcrossStepsUntilNewestChanges() {
|
||||
final long[] state = SectionAgeMath.newState();
|
||||
final long newest = 1_000L;
|
||||
final long tsQ = SectionAgeMath.quantize(400 * STEP_NANOS);
|
||||
SectionAgeMath.record(state, newest, tsQ, true);
|
||||
|
||||
assertTrue(SectionAgeMath.canSkip(state, newest, tsQ));
|
||||
assertTrue(SectionAgeMath.canSkip(state, newest, tsQ + 10_000 * STEP_NANOS), "saturation is time-independent");
|
||||
assertFalse(SectionAgeMath.canSkip(state, newest + 1, tsQ), "a new section load ends saturation");
|
||||
|
||||
SectionAgeMath.record(state, newest + 1, tsQ + STEP_NANOS, false);
|
||||
assertTrue(SectionAgeMath.canSkip(state, newest + 1, tsQ + STEP_NANOS));
|
||||
assertFalse(SectionAgeMath.canSkip(state, newest + 1, tsQ + 2 * STEP_NANOS));
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
class TerrainFragmentShaderDriftTest {
|
||||
|
||||
private static final String UPSTREAM_FSH = "/assets/sodium/shaders/blocks/block_layer_opaque.fsh";
|
||||
private static final String ANGELICA_FSH = "/assets/angelica/shaders/blocks/block_layer_opaque.fsh";
|
||||
|
||||
private static final String REPLACED_SAMPLE = "vec4 diffuseColor = texture(u_BlockTex, v_TexCoord, v_MaterialMipBias);";
|
||||
|
||||
private static String resource(String path) throws IOException {
|
||||
try (InputStream in = TerrainFragmentShaderDriftTest.class.getResourceAsStream(path)) {
|
||||
assertNotNull(in, "missing resource: " + path);
|
||||
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/** Statements with whitespace collapsed, so line wrapping and indentation don't count as drift. */
|
||||
private static List<String> normalize(String src) {
|
||||
final List<String> out = new ArrayList<>();
|
||||
final StringBuilder statement = new StringBuilder();
|
||||
for (String line : src.split("\\R")) {
|
||||
final int comment = line.indexOf("//");
|
||||
if (comment >= 0) line = line.substring(0, comment);
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) continue;
|
||||
if (line.startsWith("#")) {
|
||||
out.add(line.replaceAll("\\s+", " "));
|
||||
continue;
|
||||
}
|
||||
for (char c : line.toCharArray()) {
|
||||
statement.append(c);
|
||||
if (c == ';' || c == '{' || c == '}') {
|
||||
out.add(statement.toString().replaceAll("\\s+", " ").trim());
|
||||
statement.setLength(0);
|
||||
}
|
||||
}
|
||||
statement.append(' ');
|
||||
}
|
||||
if (!statement.toString().isBlank()) out.add(statement.toString().replaceAll("\\s+", " ").trim());
|
||||
return out;
|
||||
}
|
||||
|
||||
@Test
|
||||
void angelicaFragmentShaderContainsUpstream() throws IOException {
|
||||
final List<String> upstream = normalize(resource(UPSTREAM_FSH));
|
||||
final List<String> angelica = normalize(resource(ANGELICA_FSH));
|
||||
|
||||
assertEquals(1, upstream.stream().filter(REPLACED_SAMPLE::equals).count(), "upstream diffuse sample line changed; update REPLACED_SAMPLE and Angelica's sampler selection");
|
||||
upstream.remove(REPLACED_SAMPLE);
|
||||
|
||||
int a = 0;
|
||||
for (int u = 0; u < upstream.size(); u++) {
|
||||
final String want = upstream.get(u);
|
||||
while (a < angelica.size() && !angelica.get(a).equals(want)) a++;
|
||||
if (a == angelica.size()) {
|
||||
fail("Angelica " + ANGELICA_FSH + " is missing upstream statement " + (u + 1) + " in order: " + want);
|
||||
}
|
||||
a++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.embeddedt.embeddium.impl.gl.attribute.GlVertexAttribute;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderParser;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.QuadPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkFogMode;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkShaderBindingPoints;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkMeshFormats;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
class TerrainShaderLinkTest {
|
||||
|
||||
private static final String VSH = "sodium:blocks/block_layer_opaque.vsh";
|
||||
private static final String FSH = "angelica:blocks/block_layer_opaque.fsh";
|
||||
|
||||
private static final int MIPPED_LEVELS = 4;
|
||||
|
||||
enum Filter {
|
||||
PLAIN(false, false, false),
|
||||
TEXEL_SNAP(true, false, false),
|
||||
RGSS(true, true, false),
|
||||
ANISOTROPIC(true, false, true),
|
||||
RGSS_ANISOTROPIC(true, true, true);
|
||||
|
||||
final boolean texelSnap, rgss, anisotropic;
|
||||
|
||||
Filter(boolean texelSnap, boolean rgss, boolean anisotropic) {
|
||||
this.texelSnap = texelSnap;
|
||||
this.rgss = rgss;
|
||||
this.anisotropic = anisotropic;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, ChunkVertexType> VERTEX_TYPES = new LinkedHashMap<>();
|
||||
static {
|
||||
VERTEX_TYPES.put("VANILLA_LIKE", ChunkMeshFormats.VANILLA_LIKE);
|
||||
VERTEX_TYPES.put("COMPACT", ChunkMeshFormats.COMPACT);
|
||||
}
|
||||
|
||||
static Stream<Arguments> variants() {
|
||||
final List<Arguments> out = new ArrayList<>();
|
||||
for (ChunkFogMode fog : ChunkFogMode.values()) {
|
||||
for (String vertex : VERTEX_TYPES.keySet()) {
|
||||
for (Filter filter : Filter.values()) {
|
||||
for (boolean noMips : new boolean[] {false, true}) {
|
||||
out.add(Arguments.of(fog, vertex, filter, noMips));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.stream();
|
||||
}
|
||||
|
||||
private static ShaderConstants constants(ChunkFogMode fog, ChunkVertexType vertexType, Filter filter, boolean noMips) {
|
||||
final ShaderConstants.Builder b = ShaderConstants.builder();
|
||||
b.addAll(fog.getDefines());
|
||||
b.add("USE_FRAGMENT_DISCARD");
|
||||
b.addAll(AngelicaRenderPassConfiguration.samplerDefines(filter.rgss, filter.texelSnap, filter.anisotropic, noMips ? 0 : MIPPED_LEVELS));
|
||||
vertexType.getDefines().forEach(b::add);
|
||||
b.addAll(QuadPrimitiveType.TRIANGULATED.getDefines());
|
||||
return b.build();
|
||||
}
|
||||
|
||||
private static int compile(int type, String name, ShaderConstants constants) {
|
||||
final String src = ShaderParser.parseShader(ShaderLoader.getShaderSource(name), ShaderLoader::getShaderSource, constants);
|
||||
final int shader = GLStateManager.glCreateShader(type);
|
||||
GLStateManager.glShaderSource(shader, src);
|
||||
GLStateManager.glCompileShader(shader);
|
||||
if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) != GL11.GL_TRUE) {
|
||||
final String log = GLStateManager.glGetShaderInfoLog(shader, 8192);
|
||||
GLStateManager.glDeleteShader(shader);
|
||||
throw new AssertionError(name + " failed to compile with " + constants.getDefineStrings() + ":\n" + log);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "fog={0} vertex={1} filter={2} noMips={3}")
|
||||
@MethodSource("variants")
|
||||
void links(ChunkFogMode fog, String vertex, Filter filter, boolean noMips) {
|
||||
final ChunkVertexType vertexType = VERTEX_TYPES.get(vertex);
|
||||
final ShaderConstants constants = constants(fog, vertexType, filter, noMips);
|
||||
|
||||
final int vsh = compile(GL20.GL_VERTEX_SHADER, VSH, constants);
|
||||
int fsh = 0;
|
||||
int program = 0;
|
||||
try {
|
||||
fsh = compile(GL20.GL_FRAGMENT_SHADER, FSH, constants);
|
||||
program = GLStateManager.glCreateProgram();
|
||||
GLStateManager.glAttachShader(program, vsh);
|
||||
GLStateManager.glAttachShader(program, fsh);
|
||||
int i = 0;
|
||||
for (GlVertexAttribute attr : vertexType.getVertexFormat().getAttributes()) {
|
||||
GLStateManager.glBindAttribLocation(program, i++, attr.getName());
|
||||
}
|
||||
GLStateManager.glBindFragDataLocation(program, ChunkShaderBindingPoints.FRAG_COLOR, "fragColor");
|
||||
GLStateManager.glLinkProgram(program);
|
||||
final int linked = program;
|
||||
assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS),
|
||||
() -> "terrain program failed to link with " + constants.getDefineStrings() + ":\n" + GLStateManager.glGetProgramInfoLog(linked, 8192));
|
||||
} finally {
|
||||
if (program != 0) GLStateManager.glDeleteProgram(program);
|
||||
if (fsh != 0) GLStateManager.glDeleteShader(fsh);
|
||||
GLStateManager.glDeleteShader(vsh);
|
||||
}
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkRenderMatrices;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkRenderer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.ChunkUpdateType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSectionManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildContext;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.QuadPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.tasks.ChunkBuilderTask;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.fog.FogService;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.lists.ChunkRenderListIterable;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.lists.RenderListManager;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.sorting.CutPlaneIndex;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.TerrainRenderPass;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.Material;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.terrain.material.parameters.AlphaCutoffParameter;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkMeshFormats;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.CameraTransform;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.Viewport;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.frustum.Frustum;
|
||||
import org.joml.Vector3d;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class TreeSortCameraBookkeepingGLTest {
|
||||
|
||||
private static final Frustum ALWAYS_VISIBLE = (minX, minY, minZ, maxX, maxY, maxZ) -> true;
|
||||
|
||||
private static CommandList commandList;
|
||||
|
||||
private TestRsm rsm;
|
||||
private RenderSection section;
|
||||
private int frame;
|
||||
|
||||
private static final class TestRsm extends RenderSectionManager {
|
||||
boolean inShadowPass;
|
||||
|
||||
TestRsm(RenderPassConfiguration<?> config, CommandList commandList) {
|
||||
// -1 threads: no workers; SORT entries stay queued since updateChunks() is never called
|
||||
super(config, () -> new ChunkBuildContext(config), (device, c) -> new NoopRenderer(c), 2, commandList, 0, 16, -1, true);
|
||||
}
|
||||
|
||||
@Override protected AsyncOcclusionMode getAsyncOcclusionMode() { return AsyncOcclusionMode.NONE; }
|
||||
@Override protected boolean useFogOcclusion() { return false; }
|
||||
@Override protected boolean useRasterOcclusionCulling() { return false; }
|
||||
@Override protected boolean shouldUseOcclusionCulling(Viewport viewport, boolean spectator) { return false; }
|
||||
@Override public FogService getFogService() { throw new UnsupportedOperationException(); }
|
||||
@Override protected boolean isSectionVisuallyEmpty(int x, int y, int z) { return true; }
|
||||
@Override protected ChunkBuilderTask<ChunkBuildOutput> createRebuildTask(RenderSection render, int frame) { return null; }
|
||||
@Override public boolean isInShadowPass() { return this.inShadowPass; }
|
||||
|
||||
Vector3d cameraPosition() { return this.cameraPosition; }
|
||||
Vector3d previousCameraPosition() { return this.previousCameraPosition; }
|
||||
RenderListManager terrainLists() { return this.renderListManager; }
|
||||
RenderListManager shadowLists() { return this.shadowRenderListManager; }
|
||||
}
|
||||
|
||||
private record NoopRenderer(RenderPassConfiguration<?> getRenderPassConfiguration) implements ChunkRenderer {
|
||||
@Override
|
||||
public void render(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass pass, CameraTransform occlusionCamera, CameraTransform camera) {}
|
||||
|
||||
@Override
|
||||
public void delete(CommandList commandList) {}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void setUpDevice() {
|
||||
RenderDevice.enterManagedCode();
|
||||
commandList = RenderDevice.INSTANCE.createCommandList();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDownDevice() {
|
||||
RenderDevice.exitManagedCode();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws ReflectiveOperationException {
|
||||
final TerrainRenderPass solid = new TerrainRenderPass("tree_sort_solid", TerrainRenderPass.PipelineState.DEFAULT, false, false, false, false, ChunkMeshFormats.VANILLA_LIKE, QuadPrimitiveType.TRIANGULATED, Map.of());
|
||||
final TerrainRenderPass translucent = new TerrainRenderPass("tree_sort_translucent", TerrainRenderPass.PipelineState.DEFAULT, true, false, true, false, ChunkMeshFormats.VANILLA_LIKE, QuadPrimitiveType.TRIANGULATED, Map.of());
|
||||
final Material solidMaterial = new Material(solid, AlphaCutoffParameter.ZERO, false);
|
||||
final Material translucentMaterial = new Material(translucent, AlphaCutoffParameter.ZERO, true);
|
||||
final RenderPassConfiguration<Object> config = new RenderPassConfiguration<>(Map.of(), Map.of("solid", List.of(solid), "translucent", List.of(translucent)), solidMaterial, solidMaterial, translucentMaterial);
|
||||
|
||||
rsm = new TestRsm(config, commandList);
|
||||
rsm.onSectionAdded(0, 0, 0);
|
||||
section = rsm.getAllRenderSections().iterator().next();
|
||||
|
||||
final Field f = RenderSectionManager.class.getDeclaredField("cutPlaneIndex");
|
||||
f.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
final CutPlaneIndex<RenderSection> index = (CutPlaneIndex<RenderSection>) f.get(rsm);
|
||||
index.put(section, new float[][] { { 8f }, {}, {} }, section.getOriginX(), section.getOriginY(), section.getOriginZ());
|
||||
assertEquals(1, index.size());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (rsm != null) rsm.destroy();
|
||||
rsm = null;
|
||||
}
|
||||
|
||||
private static Viewport at(double x) {
|
||||
return new Viewport(ALWAYS_VISIBLE, new Vector3d(x, 8.5, 8.5));
|
||||
}
|
||||
|
||||
private static Viewport farShadowViewport() {
|
||||
return new Viewport(ALWAYS_VISIBLE, new Vector3d(520.5, 100.5, 520.5));
|
||||
}
|
||||
|
||||
private void update(double x) {
|
||||
rsm.update(at(x), ++frame, false);
|
||||
}
|
||||
|
||||
private void shadowThenMain(double x) {
|
||||
rsm.inShadowPass = true;
|
||||
rsm.updateForShadowPass(at(x), farShadowViewport(), ++frame, false);
|
||||
rsm.inShadowPass = false;
|
||||
assertTrue(rsm.didShadowPassRunThisFrame());
|
||||
rsm.update(at(x), frame, false);
|
||||
assertFalse(rsm.didShadowPassRunThisFrame());
|
||||
}
|
||||
|
||||
private long queued(RenderListManager lists, ChunkUpdateType type) {
|
||||
return lists.getRebuildLists().byUpdateType().get(type).stream().filter(s -> s == section).count();
|
||||
}
|
||||
|
||||
private void assertSortQueuedInTerrain() {
|
||||
assertEquals(ChunkUpdateType.SORT, section.getPendingUpdate());
|
||||
assertEquals(1, queued(rsm.terrainLists(), ChunkUpdateType.SORT));
|
||||
assertEquals(0, queued(rsm.shadowLists(), ChunkUpdateType.SORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void crossingSchedulesSort() {
|
||||
update(7.5);
|
||||
assertNull(section.getPendingUpdate());
|
||||
|
||||
update(8.5);
|
||||
assertNotNull(rsm.previousCameraPosition());
|
||||
assertNotSame(rsm.previousCameraPosition(), rsm.cameraPosition());
|
||||
assertEquals(7.5, rsm.previousCameraPosition().x);
|
||||
assertEquals(8.5, rsm.cameraPosition().x);
|
||||
assertSortQueuedInTerrain();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shadowFrameKeepsPlayerCameraBookkeeping() {
|
||||
update(7.5);
|
||||
|
||||
shadowThenMain(8.5);
|
||||
assertEquals(new Vector3d(8.5, 8.5, 8.5), rsm.cameraPosition());
|
||||
assertEquals(new Vector3d(7.5, 8.5, 8.5), rsm.previousCameraPosition());
|
||||
assertSortQueuedInTerrain();
|
||||
|
||||
shadowThenMain(8.5);
|
||||
assertNotSame(rsm.previousCameraPosition(), rsm.cameraPosition());
|
||||
assertEquals(new Vector3d(8.5, 8.5, 8.5), rsm.previousCameraPosition());
|
||||
assertEquals(new Vector3d(8.5, 8.5, 8.5), rsm.cameraPosition());
|
||||
}
|
||||
}
|
||||
-2
@@ -17,7 +17,6 @@ import org.embeddedt.embeddium.impl.gl.array.GlVertexArray;
|
||||
import org.embeddedt.embeddium.impl.gl.buffer.GlBufferUsage;
|
||||
import org.embeddedt.embeddium.impl.gl.buffer.GlMutableBuffer;
|
||||
import org.embeddedt.embeddium.impl.gl.device.CommandList;
|
||||
import org.embeddedt.embeddium.impl.gl.device.GLRenderDevice;
|
||||
import org.embeddedt.embeddium.impl.gl.device.RenderDevice;
|
||||
import org.embeddedt.embeddium.impl.gl.tessellation.GlPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.gl.tessellation.GlVertexArrayTessellation;
|
||||
@@ -183,7 +182,6 @@ class IndirectCullDrawParityTest {
|
||||
|
||||
@BeforeAll
|
||||
static void setUpDevice() {
|
||||
GLRenderDevice.VANILLA_STATE_RESETTER = () -> {};
|
||||
RenderDevice.enterManagedCode();
|
||||
commandList = RenderDevice.INSTANCE.createCommandList();
|
||||
program = buildProgram();
|
||||
|
||||
+28
@@ -21,6 +21,8 @@ import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Iterator;
|
||||
@@ -157,6 +159,23 @@ class TerrainPassPreparationTest {
|
||||
assertEquals(((long) base << 32) | 1L, ((long[]) field(expected, expected.getClass(), "regionRanges"))[0]);
|
||||
}
|
||||
|
||||
private int indexPointerMaskOf(String pass) {
|
||||
final Object state = state(pass);
|
||||
return (int) field(state, state.getClass(), "indexPointerMask");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void useUnsortedTranslucentPass() {
|
||||
storage[2].delete();
|
||||
final TerrainRenderPass pass = new TerrainRenderPass("preparation2-unsorted", null, false, true, false, false, ChunkMeshFormats.VANILLA_LIKE, QuadPrimitiveType.TRIANGULATED, Map.of());
|
||||
final Storage newStorage = new Storage(false);
|
||||
passes[2] = pass;
|
||||
storage[2] = newStorage;
|
||||
AngelicaRenderPassConfiguration.TRANSLUCENT_PASS = pass;
|
||||
final Map<TerrainRenderPass, SectionRenderDataStorage> attached = (Map<TerrainRenderPass, SectionRenderDataStorage>) field(region, RenderRegion.class, "sectionRenderData");
|
||||
attached.put(pass, newStorage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void preparesAndSelectsAllEightPassCombinations() {
|
||||
for (int mask = 0; mask < 8; mask++) {
|
||||
@@ -236,6 +255,15 @@ class TerrainPassPreparationTest {
|
||||
select(lists, 2);
|
||||
assertRange("sortedPass", 1);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "passMask={0}")
|
||||
@ValueSource(ints = {4, 5}) // translucent alone, and chained after solid
|
||||
void unsortedTranslucentPassClearsIndexPointerMask(int passMask) {
|
||||
useUnsortedTranslucentPass();
|
||||
prepare(lists(passMask));
|
||||
assertEquals(0, indexPointerMaskOf("sortedPass"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void independentlyResetCommandStorageCannotSelectOldPreparation() {
|
||||
prepare(lists(7));
|
||||
|
||||
@@ -209,11 +209,9 @@ class RwImageStoreExtractorTest {
|
||||
@Test void knownAttributeVariants_includesCeleritasTypings() {
|
||||
final var variants = RwImageStoreExtractor.CHUNK_VERTEX_ATTRS;
|
||||
assertNotNull(variants.get("a_PosId").get("vec3"));
|
||||
assertNotNull(variants.get("a_PosId").get("uvec4"));
|
||||
assertNotNull(variants.get("a_Color").get("vec4"));
|
||||
assertNotNull(variants.get("a_TexCoord").get("vec2"));
|
||||
assertNotNull(variants.get("a_LightCoord").get("uint"));
|
||||
assertNotNull(variants.get("a_LightCoord").get("ivec2"));
|
||||
assertNotNull(variants.get("mc_Entity").get("vec4"));
|
||||
assertNotNull(variants.get("mc_Entity").get("uint"));
|
||||
assertNotNull(variants.get("mc_midTexCoord").get("vec4"));
|
||||
@@ -289,15 +287,6 @@ class RwImageStoreExtractorTest {
|
||||
assertTrue(s.contains("vec4 at_midBlock;"));
|
||||
}
|
||||
|
||||
@Test void celeritas_computeContainsDeadBranchStubGlobals() {
|
||||
final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource();
|
||||
assertTrue(s.contains("uvec4 a_PosId;"), "compressed-branch stub a_PosId");
|
||||
assertTrue(s.contains("ivec2 a_LightCoord;"), "compressed-branch stub a_LightCoord");
|
||||
assertTrue(s.contains("#ifdef USE_VERTEX_COMPRESSION"), "expected #ifdef guard around dual-branch globals");
|
||||
assertTrue(s.contains("#else"));
|
||||
assertTrue(s.contains("#endif"));
|
||||
}
|
||||
|
||||
@Test void celeritas_unpackContainsCeleritasFormulas() {
|
||||
final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource();
|
||||
assertTrue(s.contains("a_PosId = vec3(uintBitsToFloat(_vg_vbuf.data[base + 0u])"));
|
||||
@@ -305,14 +294,11 @@ class RwImageStoreExtractorTest {
|
||||
assertTrue(s.contains("a_LightCoord = _vg_vbuf.data[base + 6u]"));
|
||||
assertTrue(s.contains("mc_Entity = _vg_vbuf.data[base + 10u]"), "celeritas-typed mc_Entity must be delivered as the raw packed uint");
|
||||
assertTrue(s.contains("mc_midTexCoord = vec2(float(_vg_vbuf.data[base + 7u] & 0xFFFFu),"), "mc_midTexCoord is an unnormalized ushort delivered raw by the vertex stage, so the compute prelude must not rescale it");
|
||||
assertTrue(s.contains("a_PosId = uvec4(0u, 0u, 0u, 0u)"));
|
||||
assertTrue(s.contains("a_LightCoord = ivec2(0, 0)"));
|
||||
}
|
||||
|
||||
@Test void celeritas_computeDecodesVertexGlobals() {
|
||||
final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource();
|
||||
assertTrue(s.contains("_vert_position = a_PosId;"), "uncompressed branch must decode position\n\n" + s);
|
||||
assertTrue(s.contains("_vert_position = vec3(a_PosId.xyz) * VERT_POS_SCALE + VERT_POS_OFFSET;"), "compressed branch must decode position\n\n" + s);
|
||||
assertTrue(s.contains("_vert_position = a_PosId;"), "vertex decode must assign position\n\n" + s);
|
||||
assertTrue(s.contains("_draw_id = (_vg_draw_params >> 8) & 0xFFu;"), "_get_draw_translation depends on _draw_id\n\n" + s);
|
||||
}
|
||||
|
||||
@@ -551,13 +537,6 @@ class RwImageStoreExtractorTest {
|
||||
assertTrue(s.contains("vec2 mc_midTexCoord;"));
|
||||
}
|
||||
|
||||
@Test void celeritasIfdefAttrs_emitBothVariantsGuarded() {
|
||||
final var result = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN");
|
||||
final String s = result.computeSource();
|
||||
assertTrue(s.contains("uvec4 a_PosId;"), "compressed-branch stub a_PosId\n\n" + s);
|
||||
assertTrue(s.contains("vec3 a_PosId;"), "uncompressed-branch live a_PosId\n\n" + s);
|
||||
}
|
||||
|
||||
private static final String IMAGE_STORE_AS_BRACELESS_IF_BODY_VSH = String.join("\n",
|
||||
"#version 460 core",
|
||||
"in vec4 iris_Vertex;",
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import org.embeddedt.embeddium.impl.gl.util.VertexRange;
|
||||
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.ChunkPrimitiveType;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.region.RenderRegion;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.sorting.TranslucentQuadAnalyzer;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.sorting.SortState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -41,7 +41,7 @@ class SectionRenderDataLayoutTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateSortedIndexBuffer(ByteBuffer indexBuffer, int numPrimitives, TranslucentQuadAnalyzer.SortState chunkData, float x, float y, float z) {
|
||||
public void generateSortedIndexBuffer(ByteBuffer indexBuffer, int numPrimitives, SortState chunkData, float x, float y, float z) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user