From 6c24238c393e9cea9b82de1996bc70e20a699896 Mon Sep 17 00:00:00 2001 From: Tictim Date: Mon, 4 Dec 2023 06:10:12 +0900 Subject: [PATCH] Revise bloom render ticket API (#2243) --- .../client/particle/GTBloomParticle.java | 10 +- .../client/utils/BloomEffectUtil.java | 185 ++++++++++++------ .../electric/MetaTileEntityFusionReactor.java | 90 +++++---- 3 files changed, 172 insertions(+), 113 deletions(-) diff --git a/src/main/java/gregtech/client/particle/GTBloomParticle.java b/src/main/java/gregtech/client/particle/GTBloomParticle.java index da807b47be4..bf856cc3097 100644 --- a/src/main/java/gregtech/client/particle/GTBloomParticle.java +++ b/src/main/java/gregtech/client/particle/GTBloomParticle.java @@ -10,12 +10,9 @@ public abstract class GTBloomParticle extends GTParticle implements IBloomEffect { - private final BloomEffectUtil.BloomRenderTicket ticket; - public GTBloomParticle(double posX, double posY, double posZ) { super(posX, posY, posZ); - - this.ticket = BloomEffectUtil.registerBloomRender(getBloomRenderSetup(), getBloomType(), this); + BloomEffectUtil.registerBloomRender(getBloomRenderSetup(), getBloomType(), this, this); } @Nullable @@ -23,9 +20,4 @@ public GTBloomParticle(double posX, double posY, double posZ) { @NotNull protected abstract BloomType getBloomType(); - - @Override - protected void onExpired() { - this.ticket.invalidate(); - } } diff --git a/src/main/java/gregtech/client/utils/BloomEffectUtil.java b/src/main/java/gregtech/client/utils/BloomEffectUtil.java index 1b9df83803d..d1ac6f07cc0 100644 --- a/src/main/java/gregtech/client/utils/BloomEffectUtil.java +++ b/src/main/java/gregtech/client/utils/BloomEffectUtil.java @@ -1,5 +1,7 @@ package gregtech.client.utils; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.client.particle.GTParticle; import gregtech.client.renderer.IRenderSetup; import gregtech.client.shader.Shaders; import gregtech.client.shader.postprocessing.BloomEffect; @@ -7,14 +9,16 @@ import gregtech.common.ConfigHolder; import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderGlobal; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.shader.Framebuffer; import net.minecraft.entity.Entity; import net.minecraft.launchwrapper.Launch; import net.minecraft.util.BlockRenderLayer; -import net.minecraft.world.World; import net.minecraftforge.common.util.EnumHelper; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.relauncher.Side; @@ -24,7 +28,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.apache.commons.lang3.reflect.FieldUtils; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.CheckReturnValue; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,6 +39,7 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; +import java.util.function.Predicate; @SideOnly(Side.CLIENT) public class BloomEffectUtil { @@ -53,9 +57,6 @@ public class BloomEffectUtil { private static BlockRenderLayer bloom; private static Framebuffer bloomFBO; - @Nullable - private static World currentWorld = null; - /** * @return {@link BlockRenderLayer} instance for the bloom render layer. */ @@ -79,8 +80,8 @@ public static BlockRenderLayer getRealBloomLayer() { * layers can be changed depending on external factors, such as presence of Optifine. If the actual bloom layer is * disabled, {@link BlockRenderLayer#CUTOUT} is returned instead. * - * @return {@link BlockRenderLayer} instance for the bloom render layer, or {@link BlockRenderLayer#CUTOUT} if - * bloom layer is disabled + * @return {@link BlockRenderLayer} instance for the bloom render layer, or {@link BlockRenderLayer#CUTOUT} if bloom + * layer is disabled * @see #getEffectiveBloomLayer(BlockRenderLayer) */ @NotNull @@ -110,8 +111,8 @@ public static BlockRenderLayer getEffectiveBloomLayer(BlockRenderLayer fallback) * * @param isBloomActive Whether bloom layer should be active. If this value is {@code false}, {@code fallback} layer * will be returned. Has no effect if Optifine is present. - * @return {@link BlockRenderLayer} instance for the bloom render layer, or {@link BlockRenderLayer#CUTOUT} if - * bloom layer is disabled + * @return {@link BlockRenderLayer} instance for the bloom render layer, or {@link BlockRenderLayer#CUTOUT} if bloom + * layer is disabled * @see #getEffectiveBloomLayer(boolean, BlockRenderLayer) */ @NotNull @@ -145,42 +146,107 @@ public static Framebuffer getBloomFBO() { /** *

- * Register a custom bloom render callback for subsequent world render. The render call persists until it is - * manually freed by calling {@link BloomRenderTicket#invalidate()}, or automatically freed on client world change. + * Register a custom bloom render callback for subsequent world render. The render call persists until the + * {@code metaTileEntity} is invalidated, or the ticket is manually freed by calling + * {@link BloomRenderTicket#invalidate()}. *

*

- * This method does nothing if Optifine is present. + * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned + * instead of a ticket instance. + *

+ * + * @param setup Render setup, if exists + * @param bloomType Type of the bloom + * @param render Rendering callback + * @param metaTileEntity Meta tile entity instance + * @return Ticket for the registered bloom render callback + * @throws NullPointerException if {@code bloomType == null || render == null || metaTileEntity == null} + */ + @Nullable + public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, + @NotNull BloomType bloomType, + @NotNull IBloomEffect render, + @NotNull MetaTileEntity metaTileEntity) { + Objects.requireNonNull(metaTileEntity, "metaTileEntity == null"); + return registerBloomRender(setup, bloomType, render, t -> metaTileEntity.isValid()); + } + + /** + *

+ * Register a custom bloom render callback for subsequent world render. The render call persists until the + * {@code particle} is invalidated, or the ticket is manually freed by calling + * {@link BloomRenderTicket#invalidate()}. + *

+ *

+ * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned + * instead of a ticket instance. *

* * @param setup Render setup, if exists * @param bloomType Type of the bloom * @param render Rendering callback + * @param particle Particle instance + * @return Ticket for the registered bloom render callback + * @throws NullPointerException if {@code bloomType == null || render == null || metaTileEntity == null} + */ + @Nullable + public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, + @NotNull BloomType bloomType, + @NotNull IBloomEffect render, + @NotNull GTParticle particle) { + Objects.requireNonNull(particle, "particle == null"); + return registerBloomRender(setup, bloomType, render, t -> particle.isAlive()); + } + + /** + *

+ * Register a custom bloom render callback for subsequent world render. The render call persists until it is + * manually freed by calling {@link BloomRenderTicket#invalidate()}, or invalidated by validity checker. + *

+ *

+ * This method does not register bloom render ticket when Optifine is present, and {@code null} will be returned + * instead of a ticket instance. + *

+ * + * @param setup Render setup, if exists + * @param bloomType Type of the bloom + * @param render Rendering callback + * @param validityChecker Optional validity checker; returning {@code false} causes the ticket to be invalidated. + * Checked on both pre/post render each frame. * @return Ticket for the registered bloom render callback * @throws NullPointerException if {@code bloomType == null || render == null} */ - @NotNull - @CheckReturnValue + @Nullable public static BloomRenderTicket registerBloomRender(@Nullable IRenderSetup setup, @NotNull BloomType bloomType, - @NotNull IBloomEffect render) { - BloomRenderTicket ticket = new BloomRenderTicket(setup, bloomType, render); - if (Shaders.isOptiFineShaderPackLoaded()) { - ticket.invalidate(); - } else { - SCHEDULED_BLOOM_RENDERS.add(ticket); - } + @NotNull IBloomEffect render, + @Nullable Predicate validityChecker) { + if (Shaders.isOptiFineShaderPackLoaded()) return null; + BloomRenderTicket ticket = new BloomRenderTicket(setup, bloomType, render, validityChecker); + SCHEDULED_BLOOM_RENDERS.add(ticket); return ticket; } /** - * @deprecated use ticket-based bloom render hook - * {@link #registerBloomRender(IRenderSetup, BloomType, IBloomEffect)} + * @deprecated use ticket-based bloom render hooks */ @Deprecated @ApiStatus.ScheduledForRemoval(inVersion = "2.9") public static void requestCustomBloom(IBloomRenderFast handler, Consumer render) { BloomType bloomType = BloomType.fromValue(handler.customBloomStyle()); - registerBloomRender(handler, bloomType, (b, c) -> render.accept(b)).legacy = true; + var validityChecker = new Predicate() { + + boolean invalid; + + @Override + public boolean test(BloomRenderTicket bloomRenderTicket) { + return !invalid; + } + }; + registerBloomRender(handler, bloomType, (b, c) -> { + render.accept(b); + validityChecker.invalid = true; + }, validityChecker); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -375,12 +441,22 @@ public static int renderBloomBlockLayer(RenderGlobal renderGlobal, return result; } + private static void preDraw() { + for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) { + if (!ticket.isValid()) continue; + BLOOM_RENDERS.computeIfAbsent(new BloomRenderKey(ticket.renderSetup, ticket.bloomType), + k -> new ArrayList<>()).add(ticket); + } + SCHEDULED_BLOOM_RENDERS.clear(); + } + private static void draw(@NotNull BufferBuilder buffer, @NotNull EffectRenderContext context, @NotNull List tickets) { boolean initialized = false; @Nullable IRenderSetup renderSetup = null; for (BloomRenderTicket ticket : tickets) { + ticket.checkValidity(); if (!ticket.isValid() || !ticket.render.shouldRenderBloomEffect(context)) continue; if (!initialized) { initialized = true; @@ -390,45 +466,21 @@ private static void draw(@NotNull BufferBuilder buffer, @NotNull EffectRenderCon } } ticket.render.renderBloomEffect(buffer, context); - if (ticket.legacy) ticket.invalidate(); } if (initialized && renderSetup != null) { renderSetup.postDraw(buffer); } } - private static void preDraw() { - WorldClient world = Minecraft.getMinecraft().world; - if (currentWorld != world) { - for (List list : BLOOM_RENDERS.values()) { - for (BloomRenderTicket ticket : list) { - ticket.invalidate(); - } - } - BLOOM_RENDERS.clear(); - if (currentWorld != null) { - for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) { - ticket.invalidate(); - } - SCHEDULED_BLOOM_RENDERS.clear(); - } - currentWorld = world; - } - - for (BloomRenderTicket ticket : SCHEDULED_BLOOM_RENDERS) { - if (!ticket.isValid()) continue; - BLOOM_RENDERS.computeIfAbsent(new BloomRenderKey(ticket.renderSetup, ticket.bloomType), - k -> new ArrayList<>()).add(ticket); - } - SCHEDULED_BLOOM_RENDERS.clear(); - } - private static void postDraw() { for (var it = BLOOM_RENDERS.values().iterator(); it.hasNext();) { List list = it.next(); if (!list.isEmpty()) { - if (!list.removeIf(ticket -> !ticket.isValid()) || !list.isEmpty()) continue; + if (!list.removeIf(ticket -> { + ticket.checkValidity(); + return !ticket.isValid(); + }) || !list.isEmpty()) continue; } it.remove(); @@ -444,19 +496,17 @@ public static final class BloomRenderTicket { private final IRenderSetup renderSetup; private final BloomType bloomType; private final IBloomEffect render; + @Nullable + private final Predicate validityChecker; - private boolean valid = true; - /** - * Used to mark bloom tickets made by legacy API; this ticket will be automatically expired after 1 render call. - * Remove this method in 2.9 as well as the deprecated method. - */ - private boolean legacy; + private boolean invalidated; BloomRenderTicket(@Nullable IRenderSetup renderSetup, @NotNull BloomType bloomType, - @NotNull IBloomEffect render) { + @NotNull IBloomEffect render, @Nullable Predicate validityChecker) { this.renderSetup = renderSetup; this.bloomType = Objects.requireNonNull(bloomType, "bloomType == null"); this.render = Objects.requireNonNull(render, "render == null"); + this.validityChecker = validityChecker; } @Nullable @@ -470,17 +520,22 @@ public BloomType getBloomType() { } public boolean isValid() { - return this.valid; + return !this.invalidated; } public void invalidate() { - this.valid = false; + this.invalidated = true; + } + + private void checkValidity() { + if (!this.invalidated && this.validityChecker != null && !this.validityChecker.test(this)) { + invalidate(); + } } } /** - * @deprecated use ticket-based bloom render hook - * {@link #registerBloomRender(IRenderSetup, BloomType, IBloomEffect)} + * @deprecated use ticket-based bloom render hooks */ @Deprecated @ApiStatus.ScheduledForRemoval(inVersion = "2.9") diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java index f3ab155bac6..8e667bedfcb 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFusionReactor.java @@ -3,7 +3,11 @@ import gregtech.api.GTValues; import gregtech.api.capability.GregtechDataCodes; import gregtech.api.capability.IEnergyContainer; -import gregtech.api.capability.impl.*; +import gregtech.api.capability.impl.EnergyContainerHandler; +import gregtech.api.capability.impl.EnergyContainerList; +import gregtech.api.capability.impl.FluidTankList; +import gregtech.api.capability.impl.ItemHandlerList; +import gregtech.api.capability.impl.MultiblockRecipeLogic; import gregtech.api.gui.GuiTextures; import gregtech.api.gui.ModularUI; import gregtech.api.gui.resources.TextureArea; @@ -14,7 +18,11 @@ import gregtech.api.metatileentity.IFastRenderMetaTileEntity; import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; -import gregtech.api.metatileentity.multiblock.*; +import gregtech.api.metatileentity.multiblock.IMultiblockPart; +import gregtech.api.metatileentity.multiblock.MultiblockAbility; +import gregtech.api.metatileentity.multiblock.MultiblockDisplayText; +import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; import gregtech.api.pattern.BlockPattern; import gregtech.api.pattern.FactoryBlockPattern; import gregtech.api.pattern.MultiblockShapeInfo; @@ -31,7 +39,11 @@ import gregtech.client.renderer.texture.Textures; import gregtech.client.shader.postprocessing.BloomEffect; import gregtech.client.shader.postprocessing.BloomType; -import gregtech.client.utils.*; +import gregtech.client.utils.BloomEffectUtil; +import gregtech.client.utils.EffectRenderContext; +import gregtech.client.utils.IBloomEffect; +import gregtech.client.utils.RenderBufferHelper; +import gregtech.client.utils.RenderUtil; import gregtech.common.ConfigHolder; import gregtech.common.blocks.BlockFusionCasing; import gregtech.common.blocks.BlockGlassCasing; @@ -68,21 +80,21 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.function.DoubleSupplier; public class MetaTileEntityFusionReactor extends RecipeMapMultiblockController implements IFastRenderMetaTileEntity, IBloomEffect { + protected static final int NO_COLOR = 0; + private final int tier; private EnergyContainerList inputEnergyContainers; private long heat = 0; // defined in TileEntityFusionReactor but serialized in FusionRecipeLogic - @Nullable - private Integer color; + private int fusionRingColor = NO_COLOR; private final FusionProgressSupplier progressBarSupplier; @SideOnly(Side.CLIENT) - private BloomEffectUtil.BloomRenderTicket bloomRenderTicket; + private boolean registeredBloomRenderTicket; public MetaTileEntityFusionReactor(ResourceLocation metaTileEntityId, int tier) { super(metaTileEntityId, RecipeMaps.FUSION_RECIPES); @@ -215,6 +227,21 @@ private IBlockState getCoilState() { return MetaBlocks.FUSION_CASING.getState(BlockFusionCasing.CasingType.FUSION_COIL); } + protected int getFusionRingColor() { + return this.fusionRingColor; + } + + protected boolean hasFusionRingColor() { + return this.fusionRingColor != NO_COLOR; + } + + protected void setFusionRingColor(int fusionRingColor) { + if (this.fusionRingColor != fusionRingColor) { + this.fusionRingColor = fusionRingColor; + writeCustomData(GregtechDataCodes.UPDATE_COLOR, buf -> buf.writeVarInt(fusionRingColor)); + } + } + @Override protected void formStructure(PatternMatchContext context) { long energyStored = this.energyContainer.getEnergyStored(); @@ -236,6 +263,7 @@ public String getName() { }; this.inputEnergyContainers = new EnergyContainerList(Lists.newArrayList()); this.heat = 0; + this.setFusionRingColor(NO_COLOR); } @Override @@ -268,50 +296,35 @@ protected void updateFormedValid() { if (energyAdded > 0) this.inputEnergyContainers.removeEnergy(energyAdded); } super.updateFormedValid(); - if (recipeMapWorkable.isWorking() && color == null) { + if (recipeMapWorkable.isWorking() && fusionRingColor == NO_COLOR) { if (recipeMapWorkable.getPreviousRecipe() != null && !recipeMapWorkable.getPreviousRecipe().getFluidOutputs().isEmpty()) { - int newColor = 0xFF000000 | - recipeMapWorkable.getPreviousRecipe().getFluidOutputs().get(0).getFluid().getColor(); - if (!Objects.equals(color, newColor)) { - color = newColor; - writeCustomData(GregtechDataCodes.UPDATE_COLOR, this::writeColor); - } + setFusionRingColor(0xFF000000 | + recipeMapWorkable.getPreviousRecipe().getFluidOutputs().get(0).getFluid().getColor()); } - } else if (!recipeMapWorkable.isWorking() && isStructureFormed() && color != null) { - color = null; - writeCustomData(GregtechDataCodes.UPDATE_COLOR, this::writeColor); + } else if (!recipeMapWorkable.isWorking() && isStructureFormed()) { + setFusionRingColor(NO_COLOR); } } @Override public void writeInitialSyncData(PacketBuffer buf) { super.writeInitialSyncData(buf); - writeColor(buf); + buf.writeVarInt(this.fusionRingColor); } @Override public void receiveInitialSyncData(PacketBuffer buf) { super.receiveInitialSyncData(buf); - readColor(buf); + this.fusionRingColor = buf.readVarInt(); } @Override public void receiveCustomData(int dataId, PacketBuffer buf) { - super.receiveCustomData(dataId, buf); if (dataId == GregtechDataCodes.UPDATE_COLOR) { - readColor(buf); - } - } - - private void readColor(PacketBuffer buf) { - color = buf.readBoolean() ? buf.readVarInt() : null; - } - - private void writeColor(PacketBuffer buf) { - buf.writeBoolean(color != null); - if (color != null) { - buf.writeVarInt(color); + this.fusionRingColor = buf.readVarInt(); + } else { + super.receiveCustomData(dataId, buf); } } @@ -643,18 +656,17 @@ protected void setActive(boolean active) { @Override @SideOnly(Side.CLIENT) public void renderMetaTileEntity(double x, double y, double z, float partialTicks) { - if (this.color != null && this.bloomRenderTicket == null) { - this.bloomRenderTicket = BloomEffectUtil.registerBloomRender(FusionBloomSetup.INSTANCE, getBloomType(), - this); + if (this.hasFusionRingColor() && !this.registeredBloomRenderTicket) { + this.registeredBloomRenderTicket = true; + BloomEffectUtil.registerBloomRender(FusionBloomSetup.INSTANCE, getBloomType(), this, this); } } @Override @SideOnly(Side.CLIENT) public void renderBloomEffect(@NotNull BufferBuilder buffer, @NotNull EffectRenderContext context) { - Integer c = color; - if (c == null) return; - int color = RenderUtil.interpolateColor(c, -1, Eases.QUAD_IN.getInterpolation( + if (!this.hasFusionRingColor()) return; + int color = RenderUtil.interpolateColor(this.getFusionRingColor(), -1, Eases.QUAD_IN.getInterpolation( Math.abs((Math.abs(getOffsetTimer() % 50) + context.partialTicks()) - 25) / 25)); float a = (float) (color >> 24 & 255) / 255.0F; float r = (float) (color >> 16 & 255) / 255.0F; @@ -676,7 +688,7 @@ public void renderBloomEffect(@NotNull BufferBuilder buffer, @NotNull EffectRend @Override @SideOnly(Side.CLIENT) public boolean shouldRenderBloomEffect(@NotNull EffectRenderContext context) { - return this.color != null; + return this.hasFusionRingColor(); } @Override