Add DeltaTracker (partialTick) context to PassDefinitions (#10848)

This commit is contained in:
Matt 2026-07-21 14:34:01 -04:00 committed by GitHub
parent ee1e695124
commit 66d4d888eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 73 additions and 14 deletions

View file

@ -22,7 +22,7 @@
}
this.addLateDebugPass(frame, this.levelRenderState.cameraRenderState, terrainFog, modelViewMatrix);
+ net.minecraftforge.client.FramePassManager.insertForgePasses(frame, targets, this.levelRenderState); // Forge: Modded passes are inserted here.
+ net.minecraftforge.client.FramePassManager.insertForgePasses(frame, targets, this.levelRenderState, deltaTracker); // Forge: Modded passes are inserted here.
profiler.popPush("executeFrameGraph");
frame.execute(resourceAllocator, new FrameGraphBuilder.Inspector() {
{

View file

@ -7,10 +7,14 @@ 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.client.renderer.state.level.LevelRenderState;
import net.minecraft.resources.Identifier;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -27,25 +31,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, LevelRenderState state) {
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle, LevelRenderState state, DeltaTracker deltaTracker) {
for (PassInfo info : addedPasses) {
FramePass pass = graphBuilder.addPass(info.name);
PassDefinition forgePass = info.pass;
forgePass.extracts(bundle, pass);
forgePass.extracts(bundle, pass, deltaTracker);
pass.executes(() -> forgePass.executes(state));
}
}
/// ### 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 {
/**
* 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.
*/
void extracts(LevelTargetBundle bundle, FramePass pass);
/**
* Use to define what your pass does during the render stage.
* @deprecated Prefer {@linkplain PassDefinition#extracts(LevelTargetBundle, FramePass, DeltaTracker)}
*/
@Deprecated(forRemoval = true, since="26.2")
default void extracts(LevelTargetBundle bundle, FramePass pass) {};
/**
* This method exists to do render state extraction. Your instance of a PassDefinition should have
* locals or some filled record instance that represents the render state created.
* The resulting render state should be consumed by {@linkplain PassDefinition#executes(LevelRenderState)}
* You must also use this to define which targets your pass will bind against. See PassDefinition javadocs for details.
*/
default void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker deltaTracker) {
extracts(bundle, pass);
};
/**
* Use to define what your pass does during the render stage. This should consume the render state created
* during the extracts phase.
*/
void executes(LevelRenderState state);
}

View file

@ -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.
*/

View file

@ -5,6 +5,7 @@
package net.minecraftforge.debug.client;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.LevelTargetBundle;
import net.minecraft.client.renderer.state.level.LevelRenderState;
import net.minecraftforge.client.FramePassManager;
@ -23,6 +24,8 @@ import net.minecraft.world.phys.AABB;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.framegraph.FramePass;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
@GameTestNamespace("forge")
@Mod(RenderFrameLayerTest.MODID)
@ -48,10 +51,11 @@ 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) {
FramePassManager.PassDefinition def = new FramePassManager.PassDefinition() {
@Override
public void extracts(LevelTargetBundle bundle, FramePass pass) {
public void extracts(LevelTargetBundle bundle, FramePass pass, DeltaTracker dt) {
bundle.main = pass.readsAndWrites(bundle.main);
}
@ -72,7 +76,7 @@ public class RenderFrameLayerTest extends BaseTestMod {
event.addPass(rl(MODID), def);
FramePassManager.PassDefinition def2 = new FramePassManager.PassDefinition() {
@Override
public void extracts(LevelTargetBundle bundle, FramePass pass) {
public void extracts(@NotNull LevelTargetBundle bundle, FramePass pass, DeltaTracker dt) {
bundle.main = pass.readsAndWrites(bundle.main);
}