Add DeltaTracker (partialTick) context to PassDefinitions (#10831) (#10851)

This commit is contained in:
Matt authored and GitHub committed 2026-07-21 16:09:23 -07:00
1 parent a6eea5a004
commit a42ea9bc2c
4 files changed
+72 -10

No files matched your search

@@ -30,7 +30,7 @@
}
this.addLateDebugPass(framegraphbuilder, vec3, p_407881_);
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets); // Forge: Modded passes are inserted here.
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets, p_342180_); // Forge: Modded passes are inserted here.
profilerfiller.popPush("framegraph");
framegraphbuilder.execute(p_367325_, new FrameGraphBuilder.Inspector() {
@Override
@@ -7,9 +7,11 @@ package net.minecraftforge.client;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.framegraph.FramePass;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelTargetBundle;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import java.util.ArrayList;
import java.util.List;
@@ -27,18 +29,75 @@ public class FramePassManager {
// Note: Pass order is determined automatically within FrameGraphBuilder. It's unclear what must be done to guarantee ordering.
@ApiStatus.Internal
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle) {
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle, DeltaTracker tracker) {
for (PassInfo info : addedPasses) {
FramePass pass = graphBuilder.addPass(info.name);
PassDefinition forgePass = info.pass;
forgePass.targets(bundle, pass);
forgePass.extracts(bundle, pass, tracker);
pass.executes(forgePass::executes);
}
}
/// ### A PassDefinition must satisfy 3 things.
///
/// 1. The rendering order for the purpose of translucency sorting. Read further for specific details.
/// 2. A render state definition supplier; "what" will be rendered.
/// 3. A render state definition consumer; "how" it will be rendered.
///
///
/// To satisfy #1, all FramePasses (and thus PassDefinitions) must bind against at least one target.
/// The list of targets can be found in {@linkplain LevelTargetBundle}.
/// HOWEVER!!! Only {@linkplain LevelTargetBundle#main} is used if the graphics mode is not set to Fabulous!
/// So at minimum, all passes must guarantee a fallback binding to the main target, like the below.
/// ```
/// @Override
/// void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
/// if (bundle.clouds != null) { // Perhaps we want to bind to clouds if Fabulous! is on.
/// bundle.clouds = pass.readsAndWrites(bundle.clouds);
/// }
/// // An else clause could be used, but is not mandatory. It is fine to bind to more than one target.
/// bundle.main = pass.readsAndWrites(bundle.main);
/// }
/// ```
/// </pre>
///
/// It is up to the user to decide how their pass should bind to targets, but it must always bind to at least one.
/// Failure to satisfy this requirement will result in {@linkplain FrameGraphBuilder#resolvePassOrder} exploding.
/// Custom render targets are possible but not documented, see the implementation of {@linkplain LevelTargetBundle}
/// if you want to take a crack at it.
///
/// Satisfying #2 is simple. Any state information you need to extract
/// should be done during {@linkplain PassDefinition#extracts(LevelTargetBundle, FramePass, DeltaTracker)}.
/// No actual rendering should be done at this time. This can happen before or after binding to a target.
/// The specific implementation of your render state is up to you, it can even be done with some instance variables.
///
/// Satisfying #3 is also simple, this is the actual rendering that will consume the state created
/// during the extracts phase.
@NullMarked
public interface PassDefinition {
void targets(LevelTargetBundle bundle, FramePass pass);
void executes();
/**
* Use to define which targets your pass will bind against, see {@link FramePass#reads} and {@link FramePass#readsAndWrites}
* A FramePass must bind to at least ONE target. Otherwise, you get freaky issues with >1 modded passes.
* Additionally, this method should be used for extracting render states into instance variables if desired.
*/
default void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker tracker) {
targets(bundle, pass);
}
/**
* @deprecated Prefer {@link PassDefinition#extracts}
*/
@Deprecated(forRemoval = true, since = "1.21.10")
default void targets(LevelTargetBundle bundle, FramePass pass) {}
/**
* Use to define what your pass does during the render stage
* @apiNote In 1.21.10+, LevelRenderState is used. Because that does not exist in 1.21.8, use the client level instead.
* This method is informally deprecated due to rendering changes in versions 1.21.10+
*/
default void executes(){};
}
private record PassInfo(String name, PassDefinition pass){}
@@ -13,7 +13,7 @@ import net.minecraftforge.eventbus.api.event.RecordEvent;
import org.jspecify.annotations.NullMarked;
/**
* Fired after all vanilla frame passes are added into the pass list.
* Fired during the construction of {@linkplain net.minecraft.client.renderer.LevelRenderer}.
*
* <p>This event is fired on the {@linkplain net.minecraftforge.common.MinecraftForge#EVENT_BUS main Forge event bus},
* only on the {@linkplain net.minecraftforge.fml.LogicalSide#CLIENT logical client}.
@@ -24,8 +24,9 @@ public record AddFramePassEvent() implements RecordEvent {
/**
* Adds a frame pass to pass list.
* Create a new {@linkplain FramePassManager.PassDefinition} to handle render targets and render code.
* See javadocs for details on what creating a PassDefinition entails.
*
* @param rl Resource location for frame pass name. Use RLs to avoid duplicate names.
* @param rl Identifier for frame pass name. Use RLs to avoid duplicate names.
* @param definition see usages of {@linkplain com.mojang.blaze3d.framegraph.FramePass} in {@linkplain net.minecraft.client.renderer.LevelRenderer}
* @throws IllegalArgumentException If the name is a duplicate.
*/
@@ -5,11 +5,11 @@
package net.minecraftforge.debug.client;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelTargetBundle;
import net.minecraftforge.client.FramePassManager;
import net.minecraftforge.client.event.AddFramePassEvent;
import net.minecraftforge.common.extensions.IForgeGameTestHelper;
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.gametest.GameTest;
@@ -21,6 +21,7 @@ import net.minecraft.client.renderer.ShapeRenderer;
import net.minecraft.gametest.framework.GameTestHelper;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.framegraph.FramePass;
import org.jspecify.annotations.NullMarked;
@GameTestNamespace("forge")
@@ -47,11 +48,12 @@ public class RenderFrameLayerTest extends BaseTestMod {
/**
* If this is working, two white line box cubes will be rendered at ground level in a superflat world around (0,0)
*/
@NullMarked
public static void renderTest(AddFramePassEvent event) {
var mainCamera = Minecraft.getInstance().gameRenderer.getMainCamera();
FramePassManager.PassDefinition def = new FramePassManager.PassDefinition() {
@Override
public void targets(LevelTargetBundle bundle, FramePass pass) {
public void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
bundle.main = pass.readsAndWrites(bundle.main);
}
@@ -72,7 +74,7 @@ public class RenderFrameLayerTest extends BaseTestMod {
event.addPass(rl(MODID), def);
FramePassManager.PassDefinition def2 = new FramePassManager.PassDefinition() {
@Override
public void targets(LevelTargetBundle bundle, FramePass pass) {
public void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
bundle.main = pass.readsAndWrites(bundle.main);
}