From a940acabef92ebecb484196d77bcf45e0fee07b1 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Sat, 8 Aug 2026 05:47:39 +0800 Subject: [PATCH 1/6] chore: remove ApiStatus.Internal in mixin classes, add ApiStatus.Internal to some internal api --- .../rendering/bloom/BloomParametersUbo.java | 2 ++ .../bloom/BloomPipelineParametersUbo.java | 2 ++ .../lib/v2/rendering/bloom/TransformsUbo.java | 2 ++ .../v2/rendering/blur/BlurParametersUbo.java | 2 ++ .../pipeline/CachedRenderingChunk.java | 2 ++ .../ALRCommandEncoderBackendExtension.java | 2 ++ .../pipeline/gl/GlComputePassBackend.java | 6 ++++-- .../shader/ALRComputeProgramInstance.java | 2 ++ .../shader/ALRComputeProgramInstanceKey.java | 2 ++ .../gui/renderer/BlockStatePipRenderer.java | 2 ++ .../gui/renderer/StructurePipRenderer.java | 2 ++ .../state/BlockStatePipRenderingState.java | 2 ++ .../state/DynamicTextureBlitRenderState.java | 2 ++ .../ALRIntegrationCompatMixinPlugin.java | 4 ---- ...chedBlockEntityRenderingPipelineMixin.java | 2 -- .../integration/mixins/RebuildTaskMixin.java | 2 -- .../rendering/mixins/GameRendererMixin.java | 2 -- .../mixins/GuiGraphicsExtractorMixin.java | 2 -- .../v2/rendering/mixins/GuiRendererMixin.java | 2 -- .../mixins/ItemStackRenderStateMixin.java | 2 -- .../v2/rendering/mixins/MinecraftMixin.java | 2 -- .../v2/rendering/mixins/RenderTypeMixin.java | 2 -- .../mixins/blaze3d/CommandEncoderMixin.java | 2 -- .../mixins/blaze3d/GpuDeviceMixin.java | 21 ++++++++++++++++--- .../blaze3d/gl/DirectStateAccessMixin.java | 2 -- .../blaze3d/gl/GlCommandEncoderMixin.java | 2 -- .../mixins/blaze3d/gl/GlDebugLabelMixin.java | 2 -- .../mixins/blaze3d/gl/GlDeviceMixin.java | 15 +++++++++---- .../transform/LazySyncBytecodeInjector.java | 2 ++ .../sync/transform/LazySyncTargetIndex.java | 2 ++ .../sync/transform/SyncBytecodeInjector.java | 2 ++ .../v2/sync/transform/SyncClassProcessor.java | 2 ++ .../v2/sync/transform/SyncTargetIndex.java | 2 ++ .../sync/network/payload/LazySyncPayload.java | 2 ++ .../payload/SyncConfigurationPayload.java | 2 ++ .../v2/sync/network/payload/SyncPayload.java | 2 ++ 36 files changed, 73 insertions(+), 37 deletions(-) diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomParametersUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomParametersUbo.java index 47497b31..fdc7fcad 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomParametersUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomParametersUbo.java @@ -7,9 +7,11 @@ import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; @Setter @Getter +@ApiStatus.Internal public class BloomParametersUbo extends BufferObject { public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomPipelineParametersUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomPipelineParametersUbo.java index 17be00c3..39e4a53c 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomPipelineParametersUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/BloomPipelineParametersUbo.java @@ -7,10 +7,12 @@ import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; import org.joml.Vector2f; @Getter @Setter +@ApiStatus.Internal public class BloomPipelineParametersUbo extends BufferObject { public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/TransformsUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/TransformsUbo.java index ed9203bb..fdb6b0aa 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/TransformsUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/bloom/TransformsUbo.java @@ -7,10 +7,12 @@ import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; import org.joml.Matrix4f; @Setter @Getter +@ApiStatus.Internal public class TransformsUbo extends BufferObject { public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/blur/BlurParametersUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/blur/BlurParametersUbo.java index bffa5227..7027eaf6 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/blur/BlurParametersUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/blur/BlurParametersUbo.java @@ -7,10 +7,12 @@ import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; import org.joml.Vector2f; @Setter @Getter +@ApiStatus.Internal public class BlurParametersUbo extends BufferObject { public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/cachedber/pipeline/CachedRenderingChunk.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/cachedber/pipeline/CachedRenderingChunk.java index 9d24a053..7a5bba01 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/cachedber/pipeline/CachedRenderingChunk.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/cachedber/pipeline/CachedRenderingChunk.java @@ -27,6 +27,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.ApiStatus; import org.joml.Matrix4fStack; import org.joml.Vector3f; import org.joml.Vector4f; @@ -44,6 +45,7 @@ /** * @author ZhuRuoLing */ +@ApiStatus.Internal public class CachedRenderingChunk implements VertexBufferHost { @Getter private final ChunkPos chunkPos; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRCommandEncoderBackendExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRCommandEncoderBackendExtension.java index d66777a8..78ac120e 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRCommandEncoderBackendExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRCommandEncoderBackendExtension.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; +import org.jetbrains.annotations.ApiStatus; + import com.mojang.blaze3d.buffers.GpuBufferSlice; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; import org.jetbrains.annotations.ApiStatus; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/gl/GlComputePassBackend.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/gl/GlComputePassBackend.java index ae86f694..7bdecd8c 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/gl/GlComputePassBackend.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/gl/GlComputePassBackend.java @@ -26,6 +26,8 @@ import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL46; +import java.util.function.Supplier; + @ApiStatus.Internal public class GlComputePassBackend implements ALRComputePassBackend { private final ALRGpuDeviceBackendExtension backendExtension; @@ -51,8 +53,8 @@ public void setPipeline(ALRComputePipeline pipeline) { } @Override - public void pushDebugGroup(String name) { - this.backendExtension.alrPushDebugGroup(name); + public void pushDebugGroup(Supplier message) { + this.backendExtension.alrPushDebugGroup(message); } @Override diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstance.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstance.java index ed61ac87..40701ed3 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstance.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstance.java @@ -2,7 +2,9 @@ import dev.anvilcraft.lib.v2.rendering.AnvilLibRendering; import net.minecraft.client.renderer.ShaderDefines; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Internal public record ALRComputeProgramInstance(int id, ALRComputeProgramInstanceKey key) { public static final ALRComputeProgramInstance INVALID = new ALRComputeProgramInstance( 0, diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstanceKey.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstanceKey.java index af5810b0..0685ca6b 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstanceKey.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstanceKey.java @@ -2,6 +2,8 @@ import net.minecraft.client.renderer.ShaderDefines; import net.minecraft.resources.Identifier; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Internal public record ALRComputeProgramInstanceKey(Identifier location, ShaderDefines defines) { } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/BlockStatePipRenderer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/BlockStatePipRenderer.java index 0b5f773d..0e41f791 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/BlockStatePipRenderer.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/BlockStatePipRenderer.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.gui.renderer; +import org.jetbrains.annotations.ApiStatus; + import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/StructurePipRenderer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/StructurePipRenderer.java index 030e87c3..0dfc68fc 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/StructurePipRenderer.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/renderer/StructurePipRenderer.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.gui.renderer; +import org.jetbrains.annotations.ApiStatus; + import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.systems.CommandEncoder; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/BlockStatePipRenderingState.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/BlockStatePipRenderingState.java index 8cc8a1a2..ef5564b7 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/BlockStatePipRenderingState.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/BlockStatePipRenderingState.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.gui.state; +import org.jetbrains.annotations.ApiStatus; + import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.renderer.block.BlockAndTintGetter; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/DynamicTextureBlitRenderState.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/DynamicTextureBlitRenderState.java index bf0f4f0f..167323b1 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/DynamicTextureBlitRenderState.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/gui/state/DynamicTextureBlitRenderState.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.gui.state; +import org.jetbrains.annotations.ApiStatus; + import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.vertex.VertexConsumer; import net.minecraft.client.gui.navigation.ScreenRectangle; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/ALRIntegrationCompatMixinPlugin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/ALRIntegrationCompatMixinPlugin.java index 5b4cf91f..e6490eb3 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/ALRIntegrationCompatMixinPlugin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/ALRIntegrationCompatMixinPlugin.java @@ -1,10 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.integration.mixins; import com.google.common.collect.ImmutableMap; -import dev.anvilcraft.lib.v2.rendering.integration.IrisSupport; import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.fml.loading.LoadingModList; -import org.jetbrains.annotations.ApiStatus; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; @@ -12,7 +9,6 @@ import java.util.List; import java.util.Set; -@ApiStatus.Internal public class ALRIntegrationCompatMixinPlugin implements IMixinConfigPlugin { private ImmutableMap mixinConditions; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/CachedBlockEntityRenderingPipelineMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/CachedBlockEntityRenderingPipelineMixin.java index 0b686d66..f16e1ffd 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/CachedBlockEntityRenderingPipelineMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/CachedBlockEntityRenderingPipelineMixin.java @@ -2,7 +2,6 @@ import dev.anvilcraft.lib.v2.rendering.cachedber.pipeline.CachedBlockEntityRenderingPipeline; import dev.anvilcraft.lib.v2.rendering.integration.IrisSupport; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -11,7 +10,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(CachedBlockEntityRenderingPipeline.class) -@ApiStatus.Internal public abstract class CachedBlockEntityRenderingPipelineMixin { @Shadow diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/RebuildTaskMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/RebuildTaskMixin.java index 58378ed3..0dddaeaa 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/RebuildTaskMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/integration/mixins/RebuildTaskMixin.java @@ -3,14 +3,12 @@ import dev.anvilcraft.lib.v2.rendering.cachedber.pipeline.RebuildTask; import dev.anvilcraft.lib.v2.rendering.integration.IrisSupport; import net.irisshaders.iris.vertices.ImmediateState; -import org.jetbrains.annotations.ApiStatus; 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(RebuildTask.class) -@ApiStatus.Internal public class RebuildTaskMixin { @Inject( method = "run", diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GameRendererMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GameRendererMixin.java index f255774d..b315c7ba 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GameRendererMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GameRendererMixin.java @@ -3,14 +3,12 @@ import dev.anvilcraft.lib.v2.rendering.event.MainTargetResizeEvent; import net.minecraft.client.renderer.GameRenderer; import net.neoforged.fml.ModLoader; -import org.jetbrains.annotations.ApiStatus; 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(GameRenderer.class) -@ApiStatus.Internal public class GameRendererMixin { @Inject( diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiGraphicsExtractorMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiGraphicsExtractorMixin.java index 7cd927ef..16b4ac87 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiGraphicsExtractorMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiGraphicsExtractorMixin.java @@ -14,7 +14,6 @@ import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import org.jetbrains.annotations.ApiStatus; import org.joml.Matrix3x2f; import org.joml.Matrix3x2fStack; import org.jspecify.annotations.Nullable; @@ -24,7 +23,6 @@ @SuppressWarnings("AddedMixinMembersNamePattern") @Mixin(GuiGraphicsExtractor.class) -@ApiStatus.Internal public class GuiGraphicsExtractorMixin implements GuiGraphicsExtractorExtension { @Shadow diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiRendererMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiRendererMixin.java index 251356b7..ba33ef8a 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiRendererMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/GuiRendererMixin.java @@ -17,7 +17,6 @@ import net.minecraft.client.renderer.state.gui.GuiItemRenderState; import net.minecraft.client.renderer.state.gui.GuiRenderState; import net.minecraft.util.ARGB; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -30,7 +29,6 @@ import java.util.Map; @Mixin(GuiRenderer.class) -@ApiStatus.Internal public class GuiRendererMixin { @Unique private GuiElementRenderState anvillib$renderState = null; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/ItemStackRenderStateMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/ItemStackRenderStateMixin.java index bf6e5736..23c7bf18 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/ItemStackRenderStateMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/ItemStackRenderStateMixin.java @@ -2,12 +2,10 @@ import dev.anvilcraft.lib.v2.rendering.internal.ItemStackRenderStateInternals; import net.minecraft.client.renderer.item.ItemStackRenderState; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @Mixin(ItemStackRenderState.class) -@ApiStatus.Internal public class ItemStackRenderStateMixin implements ItemStackRenderStateInternals.Extension { @Unique private float anvillib_rendering$alpha = 1f; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java index 80237e09..5bf3c8ae 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java @@ -6,7 +6,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.main.GameConfig; import net.minecraft.client.multiplayer.ClientLevel; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -15,7 +14,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(Minecraft.class) -@ApiStatus.Internal public class MinecraftMixin { @Shadow diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/RenderTypeMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/RenderTypeMixin.java index 77f6f38c..d3ec2464 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/RenderTypeMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/RenderTypeMixin.java @@ -2,12 +2,10 @@ import dev.anvilcraft.lib.v2.rendering.extension.ALRRenderTypeExtension; import net.minecraft.client.renderer.rendertype.RenderType; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @Mixin(RenderType.class) -@ApiStatus.Internal public class RenderTypeMixin implements ALRRenderTypeExtension { @Unique diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/CommandEncoderMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/CommandEncoderMixin.java index 12f97ab9..35ba5328 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/CommandEncoderMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/CommandEncoderMixin.java @@ -7,13 +7,11 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRCommandEncoderExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.MemoryBarrierFlag; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; -import org.jetbrains.annotations.ApiStatus; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @Mixin(CommandEncoder.class) -@ApiStatus.Internal public class CommandEncoderMixin implements ALRCommandEncoderExtension { @Shadow @Final diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java index 3bcd260c..b0ebc7f4 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java @@ -4,18 +4,18 @@ import com.mojang.blaze3d.systems.GpuDeviceBackend; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceBackendExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; -import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; -import org.jetbrains.annotations.ApiStatus; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; +import java.util.function.Supplier; + @SuppressWarnings("AddedMixinMembersNamePattern") @Mixin(GpuDevice.class) -@ApiStatus.Internal public class GpuDeviceMixin implements ALRGpuDeviceExtension { @Shadow @Final @@ -31,6 +31,21 @@ public void alrDestroyComputeShader(ALRComputeProgramInstance instance) { alrBackend().alrDestroyComputeShader(instance); } + @Override + public GpuQueryObject alrCreateSamplesQuery() { + return alrBackend().alrCreateSamplesQuery(); + } + + @Override + public void alrPushDebugGroup(Supplier message) { + alrBackend().alrPushDebugGroup(message); + } + + @Override + public void alrPopDebugGroup() { + alrBackend().alrPopDebugGroup(); + } + @Unique private ALRGpuDeviceBackendExtension alrBackend() { return ((ALRGpuDeviceBackendExtension) this.backend); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/DirectStateAccessMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/DirectStateAccessMixin.java index b576a1f6..2ac5bc20 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/DirectStateAccessMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/DirectStateAccessMixin.java @@ -1,14 +1,12 @@ package dev.anvilcraft.lib.v2.rendering.mixins.blaze3d.gl; import dev.anvilcraft.lib.v2.rendering.foundation.buffers.GpuBufferConstants; -import org.jetbrains.annotations.ApiStatus; import org.lwjgl.opengl.ARBShaderStorageBufferObject; 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; -@ApiStatus.Internal public class DirectStateAccessMixin { @Mixin(targets = "com.mojang.blaze3d.opengl.DirectStateAccess$Emulated") diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlCommandEncoderMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlCommandEncoderMixin.java index ef2d02f1..093efda8 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlCommandEncoderMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlCommandEncoderMixin.java @@ -9,7 +9,6 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.MemoryBarrierFlag; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.gl.GlComputePassBackend; -import org.jetbrains.annotations.ApiStatus; import org.lwjgl.opengl.ARBComputeShader; import org.lwjgl.opengl.ARBShaderImageLoadStore; import org.lwjgl.opengl.GL46; @@ -18,7 +17,6 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(targets = "com.mojang.blaze3d.opengl.GlCommandEncoder") -@ApiStatus.Internal public class GlCommandEncoderMixin implements ALRCommandEncoderBackendExtension { @Shadow @Final diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDebugLabelMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDebugLabelMixin.java index a35136fe..6c5c833d 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDebugLabelMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDebugLabelMixin.java @@ -4,7 +4,6 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.ALRDebugLabelExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import net.minecraft.util.StringUtil; -import org.jetbrains.annotations.ApiStatus; import org.lwjgl.opengl.EXTDebugLabel; import org.lwjgl.opengl.GL46; import org.lwjgl.opengl.KHRDebug; @@ -12,7 +11,6 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -@ApiStatus.Internal public class GlDebugLabelMixin { @Mixin(GlDebugLabel.class) public static class Self implements ALRDebugLabelExtension { diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java index 59d7a87b..220d78b0 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java @@ -6,7 +6,8 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeShaderManager; -import org.jetbrains.annotations.ApiStatus; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.gl.GlSamplesQuery; import org.lwjgl.opengl.ARBComputeShader; import org.lwjgl.opengl.GL46; import org.slf4j.Logger; @@ -14,8 +15,9 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import java.util.function.Supplier; + @Mixin(targets = "com.mojang.blaze3d.opengl.GlDevice") -@ApiStatus.Internal public abstract class GlDeviceMixin implements ALRGpuDeviceBackendExtension { @Shadow @@ -56,12 +58,17 @@ public ALRComputeProgramInstance alrCompileComputeShader(ALRComputeProgramInstan } @Override - public void alrPushDebugGroup(String name) { - this.debugLabels().pushDebugGroup(() -> name); + public void alrPushDebugGroup(Supplier message) { + this.debugLabels().pushDebugGroup(message); } @Override public void alrPopDebugGroup() { this.debugLabels().popDebugGroup(); } + + @Override + public GpuQueryObject alrCreateSamplesQuery() { + return new GlSamplesQuery(); + } } diff --git a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncBytecodeInjector.java b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncBytecodeInjector.java index c0f5fb7f..1b02cbe6 100644 --- a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncBytecodeInjector.java +++ b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncBytecodeInjector.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.transform; +import org.jetbrains.annotations.ApiStatus; + import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.ApiStatus; import org.objectweb.asm.Opcodes; diff --git a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncTargetIndex.java b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncTargetIndex.java index 66eb7136..598fcd23 100644 --- a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncTargetIndex.java +++ b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/LazySyncTargetIndex.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.transform; +import org.jetbrains.annotations.ApiStatus; + import lombok.extern.slf4j.Slf4j; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; diff --git a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncBytecodeInjector.java b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncBytecodeInjector.java index dd223e1b..051ab618 100644 --- a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncBytecodeInjector.java +++ b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncBytecodeInjector.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.transform; +import org.jetbrains.annotations.ApiStatus; + import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.ApiStatus; import org.objectweb.asm.Opcodes; diff --git a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncClassProcessor.java b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncClassProcessor.java index 0076127a..8e90c728 100644 --- a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncClassProcessor.java +++ b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncClassProcessor.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.transform; +import org.jetbrains.annotations.ApiStatus; + import lombok.extern.slf4j.Slf4j; import net.neoforged.neoforgespi.transformation.ClassProcessor; import net.neoforged.neoforgespi.transformation.ProcessorName; diff --git a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncTargetIndex.java b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncTargetIndex.java index 5587e836..43cbaaea 100644 --- a/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncTargetIndex.java +++ b/module.sync/processor/src/main/java/dev/anvilcraft/lib/v2/sync/transform/SyncTargetIndex.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.transform; +import org.jetbrains.annotations.ApiStatus; + import lombok.extern.slf4j.Slf4j; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; diff --git a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/LazySyncPayload.java b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/LazySyncPayload.java index 2bf7c0d5..c1622568 100644 --- a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/LazySyncPayload.java +++ b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/LazySyncPayload.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.network.payload; +import org.jetbrains.annotations.ApiStatus; + import dev.anvilcraft.lib.v2.network.packet.IInsensitiveBiPacket; import dev.anvilcraft.lib.v2.network.packet.IPacket; import dev.anvilcraft.lib.v2.sync.AnvilLibSync; diff --git a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncConfigurationPayload.java b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncConfigurationPayload.java index 4ac65ad4..6a3894c2 100644 --- a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncConfigurationPayload.java +++ b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncConfigurationPayload.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.network.payload; +import org.jetbrains.annotations.ApiStatus; + import dev.anvilcraft.lib.v2.network.packet.IClientboundPacket; import dev.anvilcraft.lib.v2.network.packet.IPacket; import dev.anvilcraft.lib.v2.sync.AnvilLibSync; diff --git a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncPayload.java b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncPayload.java index 28af73ab..df46274b 100644 --- a/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncPayload.java +++ b/module.sync/src/main/java/dev/anvilcraft/lib/v2/sync/network/payload/SyncPayload.java @@ -1,5 +1,7 @@ package dev.anvilcraft.lib.v2.sync.network.payload; +import org.jetbrains.annotations.ApiStatus; + import dev.anvilcraft.lib.v2.network.packet.IInsensitiveBiPacket; import dev.anvilcraft.lib.v2.network.packet.IPacket; import dev.anvilcraft.lib.v2.sync.AnvilLibSync; From 6ca7cd75ebde4b1e21e2319bb9672857a88e3766 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Sat, 8 Aug 2026 05:48:42 +0800 Subject: [PATCH 2/6] feat(rendering): add occlusion culling part 1 --- .../lib/v2/rendering/ALROptimizations.java | 17 ++ .../lib/v2/rendering/ALRPipelines.java | 18 ++ .../blaze3d/ALRGpuDeviceBackendExtension.java | 11 +- .../blaze3d/ALRGpuDeviceExtension.java | 9 + .../compute/pipeline/ALRComputePass.java | 7 +- .../pipeline/ALRComputePassBackend.java | 7 +- .../shader/ALRComputeShaderManager.java | 2 + .../blaze3d/query/GpuQueryObject.java | 14 ++ .../blaze3d/query/gl/GlSamplesQuery.java | 72 ++++++++ .../foundation/GpuReusableResource.java | 13 ++ .../GpuReusableResourceRingBuffer.java | 43 +++++ .../rendering/foundation/LoopResetPool.java | 31 ++++ .../rendering/foundation/SimpleResetPool.java | 97 +++++++++++ .../buffers/ubo/FullTransformsUbo.java | 38 +++++ .../occlusion/OcclusionCuller.java | 35 ++++ .../optimization/occlusion/OcclusionKey.java | 45 +++++ .../occlusion/OcclusionMethod.java | 38 +++++ .../hiz/HierarchicalZOcculusionCuller.java | 28 +++ .../occlusion/query/FrameState.java | 152 +++++++++++++++++ .../query/GpuQueryOcclusionCuller.java | 84 +++++++++ .../query/GpuSampleQueryRingBuffer.java | 18 ++ .../occlusion/query/QueryBufferPack.java | 160 ++++++++++++++++++ .../query/QueryBufferPackRingBuffer.java | 14 ++ .../lib/v2/rendering/util/MemoryAccess.java | 79 +++++++++ .../shaders/core/occlusion_query.fsh | 7 + .../shaders/core/occlusion_query.vsh | 12 ++ 26 files changed, 1043 insertions(+), 8 deletions(-) create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/GpuQueryObject.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResource.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/LoopResetPool.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/SimpleResetPool.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionKey.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.fsh create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.vsh diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java new file mode 100644 index 00000000..351e3e59 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java @@ -0,0 +1,17 @@ +package dev.anvilcraft.lib.v2.rendering; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; +import org.jspecify.annotations.Nullable; + +public class ALROptimizations { + private static OcclusionCuller occlusionCuller; + + @Nullable + public static OcclusionCuller getOcclusionCuller() { + if (occlusionCuller == null) { + occlusionCuller = OcclusionCuller.createInstance(RenderSystem.getDevice()); + } + return occlusionCuller; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRPipelines.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRPipelines.java index c1324c99..6332d51e 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRPipelines.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRPipelines.java @@ -2,17 +2,22 @@ import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.DepthStencilState; import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.CompareOp; import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormatElement; +import dev.anvilcraft.lib.v2.rendering.bloom.TransformsUbo; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.RegisterRenderPipelinesEvent; import org.jetbrains.annotations.ApiStatus; +import java.util.Optional; + @EventBusSubscriber(Dist.CLIENT) public class ALRPipelines { public static final RenderPipeline.Snippet POST_PASS = RenderPipeline.builder() @@ -76,6 +81,17 @@ public class ALRPipelines { .withCull(false) .build(); + public static final RenderPipeline OCCLUSION_QUERY = RenderPipeline.builder() + .withLocation(AnvilLibRendering.location("occlusion_query")) + .withVertexFormat(DefaultVertexFormat.POSITION, VertexFormat.Mode.QUADS) + .withUniform("Transforms", UniformType.UNIFORM_BUFFER) + .withVertexShader(AnvilLibRendering.location("core/occlusion_query")) + .withFragmentShader(AnvilLibRendering.location("core/occlusion_query")) + .withColorTargetState(new ColorTargetState(Optional.empty(), ColorTargetState.WRITE_NONE)) + .withDepthStencilState(new DepthStencilState(CompareOp.LESS_THAN_OR_EQUAL, false)) + .withCull(true) + .build(); + @ApiStatus.Internal @SubscribeEvent @@ -86,5 +102,7 @@ public static void on(RegisterRenderPipelinesEvent event) { event.registerPipeline(UPSAMPLE); event.registerPipeline(SDF_GRAPHICS); + + event.registerPipeline(OCCLUSION_QUERY); } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java index b8d8a7c8..f2c59eb4 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java @@ -1,9 +1,12 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; -import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; +import org.jetbrains.annotations.ApiStatus; + import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; -import org.jetbrains.annotations.ApiStatus; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; + +import java.util.function.Supplier; @ApiStatus.Internal public interface ALRGpuDeviceBackendExtension { @@ -11,7 +14,9 @@ public interface ALRGpuDeviceBackendExtension { void alrDestroyComputeShader(ALRComputeProgramInstance instance); - void alrPushDebugGroup(String name); + void alrPushDebugGroup(Supplier name); void alrPopDebugGroup(); + + GpuQueryObject alrCreateSamplesQuery(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java index 184600c1..7d1e80eb 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java @@ -2,9 +2,18 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; + +import java.util.function.Supplier; public interface ALRGpuDeviceExtension { ALRComputeProgramInstance alrCompileComputeShader(ALRComputeProgramInstanceKey instanceKey); void alrDestroyComputeShader(ALRComputeProgramInstance instance); + + GpuQueryObject alrCreateSamplesQuery(); + + void alrPushDebugGroup(Supplier message); + + void alrPopDebugGroup(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java index db71a687..4b78ec51 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java @@ -7,6 +7,7 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding; import java.util.List; +import java.util.function.Supplier; public class ALRComputePass implements AutoCloseable { private ALRComputePipeline pipeline; @@ -16,11 +17,11 @@ public ALRComputePass(ALRComputePassBackend backend) { this.backend = backend; } - public void pushDebugGroup(String name) { + public void pushDebugGroup(Supplier name) { this.backend.pushDebugGroup(name); } - public void popDebugGroup(String name) { + public void popDebugGroup() { this.backend.popDebugGroup(); } @@ -33,7 +34,7 @@ public void dispatchWorkgroups( int groupCountY, int groupCountZ ) { - this.backend.pushDebugGroup("Compute " + pipeline.identifier()); + this.backend.pushDebugGroup(() -> "ALRComputePass " + pipeline.identifier()); this.backend.dispatchWorkgroups(groupCountX, groupCountY, groupCountZ); this.backend.popDebugGroup(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java index ec56de85..f5547aff 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java @@ -1,15 +1,18 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline; -import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.textures.GpuTexture; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.MemoryBarrierFlag; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding; +import org.jetbrains.annotations.ApiStatus; +import java.util.function.Supplier; + +@ApiStatus.Internal public interface ALRComputePassBackend { void setPipeline(ALRComputePipeline pipeline); - void pushDebugGroup(String name); + void pushDebugGroup(Supplier name); void popDebugGroup(); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java index 88584987..7dd02580 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java @@ -16,6 +16,7 @@ import net.minecraft.util.profiling.ProfilerFiller; import net.neoforged.fml.ModLoader; import org.apache.commons.io.IOUtils; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.UnknownNullability; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -26,6 +27,7 @@ import java.util.Map; @Slf4j +@ApiStatus.Internal public class ALRComputeShaderManager extends SimplePreparableReloadListener { public static final ALRComputeShaderManager INSTANCE = new ALRComputeShaderManager(); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/GpuQueryObject.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/GpuQueryObject.java new file mode 100644 index 00000000..6d408faa --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/GpuQueryObject.java @@ -0,0 +1,14 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query; + +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResource; + +public interface GpuQueryObject extends GpuReusableResource { + + void begin(); + + void end(); + + long getValue(); + + void close(); +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java new file mode 100644 index 00000000..f9a6f5c9 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java @@ -0,0 +1,72 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.gl; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.util.MemoryAccess; +import org.jetbrains.annotations.ApiStatus; +import org.lwjgl.opengl.GL46; +import org.lwjgl.system.MemoryStack; + +@ApiStatus.Internal +public class GlSamplesQuery implements GpuQueryObject { + + private final int id; + private long result = -1; + private boolean closed = false; + private boolean acquired = false; + + public GlSamplesQuery() { + this.id = GL46.glGenQueries(); + } + + @Override + public void begin() { + GL46.glBeginQuery(GL46.GL_ANY_SAMPLES_PASSED, id); + } + + @Override + public void end() { + GL46.glEndQuery(id); + } + + @Override + public long getValue() { + RenderSystem.assertOnRenderThread(); + if (this.closed) { + throw new IllegalStateException("GlTimerQuery is closed"); + } else { + if (result == -1) { + try (MemoryStack stack = MemoryStack.stackPush()) { + long buffer = stack.nmalloc(4); + GL46.glGetQueryObjectuiv(id, GL46.GL_QUERY_RESULT, buffer); + this.result = MemoryAccess.getLong(buffer); + } + } + } + return this.result; + } + + @Override + public void close() { + if (!closed) { + GL46.glDeleteQueries(id); + } + this.closed = true; + } + + @Override + public void acquire() { + this.acquired = true; + } + + @Override + public void release() { + this.acquired = false; + this.result = -1; + } + + @Override + public boolean isAcquired() { + return this.acquired; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResource.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResource.java new file mode 100644 index 00000000..4e729cfe --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResource.java @@ -0,0 +1,13 @@ +package dev.anvilcraft.lib.v2.rendering.foundation; + +public interface GpuReusableResource extends AutoCloseable { + + void acquire(); + + void release(); + + boolean isAcquired(); + + @Override + void close(); +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java new file mode 100644 index 00000000..101f23e8 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java @@ -0,0 +1,43 @@ +package dev.anvilcraft.lib.v2.rendering.foundation; + +public abstract class GpuReusableResourceRingBuffer extends LoopResetPool { + + public GpuReusableResourceRingBuffer(int size, C context) { + super(size, context); + } + + @Override + public void release(T query) { + query.release(); + } + + @Override + public void destroy(T query) { + query.close(); + } + + @Override + public void onAcquire(T query) { + query.acquire(); + } + + @Override + public boolean isAvailable(T query) { + return !query.isAcquired(); + } + + @Override + public T fail(boolean createInstanceIfAllAcquired) { + int index = 0; + + if (createInstanceIfAllAcquired) { + index = this.size; + expand(); + } + + T query = this.get(index); + query.acquire(); + + return query; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/LoopResetPool.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/LoopResetPool.java new file mode 100644 index 00000000..29689b8b --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/LoopResetPool.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Argon4W + * SPDX-License-Identifier: MIT + */ +package dev.anvilcraft.lib.v2.rendering.foundation; + +import org.jspecify.annotations.Nullable; + +/// @author Argon4W +public abstract class LoopResetPool extends SimpleResetPool { + + public LoopResetPool(int size, C context) { + super(size, context); + } + + @Override + @Nullable + @SuppressWarnings("unchecked") + public T acquire(boolean createInstanceIfAllAcquired) { + for (int i = 0; i < size; i++) { + T t = (T) pool[i]; + + if (this.isAvailable(t)) { + this.onAcquire(t); + return t; + } + } + + return this.fail(createInstanceIfAllAcquired); + } +} \ No newline at end of file diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/SimpleResetPool.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/SimpleResetPool.java new file mode 100644 index 00000000..28a27be8 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/SimpleResetPool.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) Argon4W + * SPDX-License-Identifier: MIT + */ +package dev.anvilcraft.lib.v2.rendering.foundation; + +import lombok.Getter; +import org.jspecify.annotations.Nullable; + +import java.util.Arrays; + +/// @author Argon4W +public abstract class SimpleResetPool { + + @Getter protected final C context; + + @Getter protected Object[] pool; + @Getter protected int cursor; + protected int size; + + public SimpleResetPool(int size, C context) { + this.size = size; + this.pool = new Object[size]; + this.context = context; + + this.cursor = 0; + + for (int i = 0; i < this.size; i++) { + this.pool[i] = createInstance(this.context, i); + } + } + + public abstract void onAcquire (T t); + + protected abstract T createInstance (C context, int i); + + @Nullable + protected abstract T fail (boolean createInstanceIfAllAcquired); + + protected abstract void release (T t); + + protected abstract void destroy (T t); + + protected abstract boolean isAvailable (T t); + + @Nullable + public T acquire() { + return this.acquire(true); + } + + @SuppressWarnings("unchecked") + @Nullable + public T acquire(boolean createInstanceIfAllAcquired) { + if (this.cursor < this.size) { + T t = (T) this.pool[this.cursor ++]; + + if (this.isAvailable(t)) { + this.onAcquire(t); + return t; + } + } + + return this.fail(createInstanceIfAllAcquired); + } + + @SuppressWarnings("unchecked") + public T get(int index) { + return (T) this.pool[index]; + } + + protected void expand() { + int old = this.size; + + this.size = old * 2; + this.pool = Arrays.copyOf(this.pool, this.size); + + for (int i = old; i < size; i ++) { + this.pool[i] = this.createInstance(context, i); + } + } + + @SuppressWarnings("unchecked") + public void releaseAll() { + for (int i = 0; i < this.cursor; i++) { + release((T) this.pool[i]); + } + + cursor = 0; + } + + @SuppressWarnings("unchecked") + public void destroyAll() { + for (int i = 0; i < this.size; i++) { + this.destroy((T) this.pool[i]); + } + } +} \ No newline at end of file diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java new file mode 100644 index 00000000..c6edcc82 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java @@ -0,0 +1,38 @@ +package dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo; + +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutDefinition; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutEntry; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; +import org.joml.Matrix4f; + +@Setter +@Getter +@ApiStatus.Internal +public class FullTransformsUbo extends BufferObject { + + public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( + BufferObjectLayoutEntry.ofMat4f().forGetter(FullTransformsUbo::getProjMat).build(), + BufferObjectLayoutEntry.ofMat4f().forGetter(FullTransformsUbo::getModelViewMat).build() + ); + + public static final int SIZE = DEFINITION.size(BufferLayout.STD140); + + private Matrix4f projMat; + private Matrix4f modelViewMat; + + public FullTransformsUbo() { + super(BufferLayout.STD140, ShaderBufferObjectUsage.UBO); + this.projMat = new Matrix4f(); + this.modelViewMat = new Matrix4f(); + } + + @Override + protected BufferObjectLayoutDefinition getDefinition() { + return DEFINITION; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java new file mode 100644 index 00000000..c634a81b --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java @@ -0,0 +1,35 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion; + +import com.mojang.blaze3d.systems.GpuDevice; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import org.jspecify.annotations.Nullable; + +public interface OcclusionCuller { + + void beginFrame(); + + void submitFeatureKey(OcclusionKey key, Object feature); + + void processFeatures(CameraRenderState camera); + + boolean shouldDraw(OcclusionKey key, Object feature); + + @SuppressWarnings("ConstantValue") + @Nullable + static OcclusionCuller createInstance(GpuDevice device) { + OcclusionCuller instance; + if (OcclusionMethod.HIERARCHICAL_Z.isSupported() + && (instance = OcclusionMethod.HIERARCHICAL_Z.createInstance(device)) != null + ) { + return instance; + } + + if (OcclusionMethod.GPU_QUERY.isSupported() + && (instance = OcclusionMethod.GPU_QUERY.createInstance(device)) != null + ) { + return instance; + } + + return null; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionKey.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionKey.java new file mode 100644 index 00000000..7f297372 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionKey.java @@ -0,0 +1,45 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion; + +import lombok.Getter; +import lombok.Setter; +import net.minecraft.world.phys.AABB; + +import java.util.function.Supplier; + +/// Stable handle for associating one logical render feature with occlusion +/// state across frames. Reuse the same instance for the feature and update +/// its bounding box as needed; identity, not bounding-box equality, defines +/// the key. +public class OcclusionKey { + + @Getter + @Setter + private AABB boundingBox; + + @Getter + private final Supplier name; + + public OcclusionKey(AABB boundingBox) { + this.boundingBox = boundingBox; + this.name = OcclusionKey::defaultName; + } + + public OcclusionKey(Supplier name, AABB boundingBox) { + this.name = name; + this.boundingBox = boundingBox; + } + + @Override + public final boolean equals(Object obj) { + return this == obj; + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + private static String defaultName() { + return "OcclusionKey"; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java new file mode 100644 index 00000000..23bc7495 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java @@ -0,0 +1,38 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion; + +import com.mojang.blaze3d.systems.GpuDevice; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.ALRComputeCapabilities; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query.GpuQueryOcclusionCuller; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public enum OcclusionMethod { + GPU_QUERY { + @Override + public boolean isSupported() { + return true; + } + + @Override + public @NonNull OcclusionCuller createInstance(GpuDevice device) { + return new GpuQueryOcclusionCuller((ALRGpuDeviceExtension) device); + } + }, HIERARCHICAL_Z { + @Override + public boolean isSupported() { + return ALRComputeCapabilities.isComputeSupported(); + } + + @Override + public @Nullable OcclusionCuller createInstance(GpuDevice device) { + return null; + } + }; + + public abstract boolean isSupported(); + + @Nullable + public abstract OcclusionCuller createInstance(GpuDevice device); + +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java new file mode 100644 index 00000000..9872c395 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java @@ -0,0 +1,28 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; + +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.state.level.CameraRenderState; + +public class HierarchicalZOcculusionCuller implements OcclusionCuller { + @Override + public void beginFrame() { + + } + + @Override + public void submitFeatureKey(OcclusionKey key, Object feature) { + + } + + @Override + public void processFeatures(CameraRenderState camera) { + + } + + @Override + public boolean shouldDraw(OcclusionKey key, Object feature) { + return false; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java new file mode 100644 index 00000000..d8571f25 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java @@ -0,0 +1,152 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.anvilcraft.lib.v2.rendering.ALRPipelines; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo.FullTransformsUbo; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; +import it.unimi.dsi.fastutil.objects.Reference2LongLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2LongMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.world.phys.AABB; +import org.joml.Matrix4f; +import org.joml.Vector3f; + +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.Set; + +class FrameState implements AutoCloseable { + private final Map keySamplesMap = new IdentityHashMap<>(); + private final Reference2LongMap results = new Reference2LongLinkedOpenHashMap<>(); + private final Set keys = new HashSet<>(); + private final GpuQueryOcclusionCuller owner; + + public FrameState(GpuQueryOcclusionCuller owner) { + this.owner = owner; + } + + public void addKey(OcclusionKey key) { + GpuQueryObject gpuSamplesQuery = this.keySamplesMap.get(key); + if (gpuSamplesQuery == null) { + this.keySamplesMap.put(key, owner.acquireQuery()); + } + } + + public boolean shouldDraw(OcclusionKey key) { + return results.getOrDefault(key, 0) > 0; + } + + public void fetchResults() { + for (Map.Entry entry : keySamplesMap.entrySet()) { + results.put(entry.getKey(), entry.getValue().getValue()); + } + } + + @SuppressWarnings("DataFlowIssue") + public void runQueries(CameraRenderState camera) { + QueryBufferPack bufferPack = this.owner.acquireBuffer(); + FullTransformsUbo transformsUbo = bufferPack.transformsUbo(); + + transformsUbo.getProjMat().set(camera.projectionMatrix); + + CommandEncoder commandEncoder = this.owner.getCommandEncoder(); + RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); + + RenderSystem.AutoStorageIndexBuffer sequentialBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); + GpuBuffer buffer = sequentialBuffer.getBuffer(6 * 6); + VertexFormat.IndexType type = sequentialBuffer.type(); + + GpuDevice device = RenderSystem.getDevice(); + + int instanceId = 0; + + ALRGpuDeviceExtension deviceExtension = (ALRGpuDeviceExtension) device; + + deviceExtension.alrPushDebugGroup(() -> "Gpu Occlusion Query Draw"); + + for (Map.Entry entry : keySamplesMap.entrySet()) { + int finalInstanceId = instanceId; + + OcclusionKey key = entry.getKey(); + GpuQueryObject query = entry.getValue(); + + Matrix4f modelViewMat = transformsUbo.getModelViewMat(); + modelViewMat.set(camera.viewRotationMatrix); + modelViewMat.translate( + (float) -camera.pos.x, + (float) -camera.pos.y, + (float) -camera.pos.z + ); + + AABB boundingBox = key.getBoundingBox(); + + Vector3f min = new Vector3f( + (float) boundingBox.minX, + (float) boundingBox.minY, + (float) boundingBox.minZ + ); + + Vector3f max = new Vector3f( + (float) boundingBox.maxX, + (float) boundingBox.maxY, + (float) boundingBox.maxZ + ); + + // ChatGPT can make mistakes. Check important info. + Matrix4f transformation = new Matrix4f() + .translate(min) + .scale( + max.x - min.x, + max.y - min.y, + max.z - min.z + ); + + modelViewMat.mul(transformation); + + transformsUbo.upload(commandEncoder, bufferPack.transformsBuffer().slice()); + + try (RenderPass renderPass = commandEncoder.createRenderPass( + () -> "Gpu Occlusion Query Draw #" + finalInstanceId, + target.getColorTextureView(), + OptionalInt.empty(), + target.getDepthTextureView(), + OptionalDouble.empty() + )) { + renderPass.setPipeline(ALRPipelines.OCCLUSION_QUERY); + + renderPass.setVertexBuffer(0, bufferPack.vertexBuffer()); + renderPass.setIndexBuffer(buffer, type); + + renderPass.setUniform("Transforms", bufferPack.transformsBuffer()); + + + query.begin(); + renderPass.drawIndexed(0, 0, 6 * 6, 1); + query.end(); + } + } + + deviceExtension.alrPopDebugGroup(); + + this.owner.releaseBuffer(bufferPack); + } + + @Override + public void close() { + for (GpuQueryObject value : keySamplesMap.values()) { + owner.releaseQuery(value); + } + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java new file mode 100644 index 00000000..c15f2634 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java @@ -0,0 +1,84 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; +import lombok.Getter; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.state.level.CameraRenderState; + +public class GpuQueryOcclusionCuller implements OcclusionCuller { + + @Getter + private final CommandEncoder commandEncoder; + private final GpuSampleQueryRingBuffer sampleQueryPool; + private final QueryBufferPackRingBuffer bufferPackPool; + private final ALRGpuDeviceExtension extension; + + private FrameState previousFrameState = null; + private FrameState currentFrameState = null; + + public GpuQueryOcclusionCuller(ALRGpuDeviceExtension extension) { + this.extension = extension; + this.sampleQueryPool = new GpuSampleQueryRingBuffer(extension); + GpuDevice gpuDevice = (GpuDevice) extension; + this.commandEncoder = gpuDevice.createCommandEncoder(); + this.bufferPackPool = new QueryBufferPackRingBuffer( + new QueryBufferPack.CreationContext( + commandEncoder, + gpuDevice + ) + ); + } + + @Override + public void beginFrame() { + if (this.currentFrameState == null) { + this.currentFrameState = new FrameState(this); + return; + } + if (this.previousFrameState != null) { + this.previousFrameState.close(); + } + this.previousFrameState = this.currentFrameState; + this.previousFrameState.fetchResults(); + this.currentFrameState = new FrameState(this); + } + + @Override + public void submitFeatureKey(OcclusionKey key, Object feature) { + this.currentFrameState.addKey(key); + } + + @Override + public void processFeatures(CameraRenderState camera) { + this.currentFrameState.runQueries(camera); + } + + @Override + public boolean shouldDraw(OcclusionKey key, Object feature) { + if (previousFrameState == null) { + return false; + } + return this.previousFrameState.shouldDraw(key); + } + + public GpuQueryObject acquireQuery() { + return this.sampleQueryPool.acquire(); + } + + public void releaseQuery(GpuQueryObject query) { + this.sampleQueryPool.release(query); + } + + public QueryBufferPack acquireBuffer() { + return this.bufferPackPool.acquire(); + } + + public void releaseBuffer(QueryBufferPack bufferPack) { + this.bufferPackPool.release(bufferPack); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java new file mode 100644 index 00000000..cf18ddb0 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java @@ -0,0 +1,18 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourceRingBuffer; +import org.jspecify.annotations.NonNull; + +public class GpuSampleQueryRingBuffer extends GpuReusableResourceRingBuffer { + public GpuSampleQueryRingBuffer(ALRGpuDeviceExtension context) { + super(16, context); + } + + @Override + @NonNull + protected GpuQueryObject createInstance(@NonNull ALRGpuDeviceExtension context, int i) { + return context.alrCreateSamplesQuery(); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java new file mode 100644 index 00000000..02655a06 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java @@ -0,0 +1,160 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResource; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo.FullTransformsUbo; + +public final class QueryBufferPack implements GpuReusableResource { + + /// each bounding box 3 * 4 * 4 * 6 byte = 288 byte, + /// total 4096 bounding boxes = 1.1 MiB + /// + /// no that's shit, + /// use single unit box and model view matrix instead + /// ((3 float * 4) for each quad * 6) for cube * 1 + public static final int DEFAULT_VERTEX_BUFFER_SIZE = 3 * 4 * 4 * 6; + + private final GpuBuffer vertexBuffer; + private final GpuBuffer transformsBuffer; + private final FullTransformsUbo transformsUbo; + + private boolean closed = false; + private boolean acquired = false; + + public QueryBufferPack( + GpuBuffer vertexBuffer, + GpuBuffer transformsBuffer, + FullTransformsUbo transformsUbo + ) { + this.vertexBuffer = vertexBuffer; + this.transformsBuffer = transformsBuffer; + this.transformsUbo = transformsUbo; + } + + + public static QueryBufferPack newInstance(CreationContext context, int index) { + QueryBufferPack bufferPack = new QueryBufferPack( + context.device.createBuffer( + () -> "Gpu Occlusion Query Vertex Buffer #" + index, + GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_COPY_DST, + DEFAULT_VERTEX_BUFFER_SIZE + ), + context.device.createBuffer( + () -> "Gpu Occlusion Query Uniform Buffer #" + index, + GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_COPY_DST, + FullTransformsUbo.SIZE + ), + new FullTransformsUbo() + ); + + prepareMesh(context, bufferPack); + + return bufferPack; + } + + private static void prepareMesh(CreationContext context, QueryBufferPack bufferPack) { + float x0 = 0; + float y0 = 0; + float z0 = 0; + float x1 = 1; + float y1 = 1; + float z1 = 1; + + BufferBuilder bufferBuilder = Tesselator.getInstance() + .begin( + VertexFormat.Mode.QUADS, + DefaultVertexFormat.POSITION + ); + + // z+ + bufferBuilder.addVertex(x0, y0, z1); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x0, y1, z1); + + // z- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x1, y1, z0); + bufferBuilder.addVertex(x1, y0, z0); + + // x+ + bufferBuilder.addVertex(x1, y0, z0); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x1, y1, z0); + + // x- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x0, y1, z1); + bufferBuilder.addVertex(x0, y0, z1); + + // y+ + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x0, y1, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x1, y1, z0); + + // y- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x1, y0, z0); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x0, y0, z1); + + MeshData orThrow = bufferBuilder.buildOrThrow(); + + context.commandEncoder.writeToBuffer(bufferPack.vertexBuffer.slice(), orThrow.vertexBuffer()); + + orThrow.close(); + } + + @Override + public void acquire() { + this.acquired = true; + } + + @Override + public void release() { + this.acquired = false; + } + + @Override + public boolean isAcquired() { + return this.acquired; + } + + @Override + public void close() { + if (!closed) { + this.vertexBuffer.close(); + this.transformsBuffer.close(); + this.closed = true; + } + } + + public GpuBuffer vertexBuffer() { + return vertexBuffer; + } + + public GpuBuffer transformsBuffer() { + return transformsBuffer; + } + + public FullTransformsUbo transformsUbo() { + return transformsUbo; + } + + public record CreationContext( + CommandEncoder commandEncoder, + GpuDevice device + ) { + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java new file mode 100644 index 00000000..3b179583 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java @@ -0,0 +1,14 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourceRingBuffer; + +public class QueryBufferPackRingBuffer extends GpuReusableResourceRingBuffer { + public QueryBufferPackRingBuffer(QueryBufferPack.CreationContext context) { + super(2, context); + } + + @Override + protected QueryBufferPack createInstance(QueryBufferPack.CreationContext context, int i) { + return QueryBufferPack.newInstance(context, i); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java new file mode 100644 index 00000000..8e5c9009 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java @@ -0,0 +1,79 @@ +package dev.anvilcraft.lib.v2.rendering.util; + +import org.lwjgl.system.Pointer; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +/// @author IMS212 +@SuppressWarnings("removal") +public class MemoryAccess { + private static final Unsafe UNSAFE = getUnsafe(); + private static final boolean BITS32 = Pointer.BITS32; + + private static Unsafe getUnsafe() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + public static void putInt(long address, int value) { + UNSAFE.putInt(address, value); + } + + public static void putFloat(long address, float value) { + UNSAFE.putFloat(address, value); + } + + public static void putLong(long address, long value) { + UNSAFE.putLong(address, value); + } + + public static void putShort(long address, short value) { + UNSAFE.putShort(address, value); + } + + public static void putByte(long address, byte b) { + UNSAFE.putByte(address, b); + } + + public static int getInt(long address) { + return UNSAFE.getInt(address); + } + + public static float getFloat(long address) { + return UNSAFE.getFloat(address); + } + + public static long getLong(long address) { + return UNSAFE.getLong(address); + } + + public static short getShort(long address) { + return UNSAFE.getShort(address); + } + + public static byte getByte(long address) { + return UNSAFE.getByte(address); + } + + public static void putAddress(long address, long value) { + if (BITS32) { + UNSAFE.putInt(address, (int) value); + } else { + UNSAFE.putLong(address, value); + } + } + + public static long getAddress(long address) { + if (BITS32) { + return UNSAFE.getInt(address) & 0xFFFF_FFFFL; + } else { + return UNSAFE.getLong(address); + } + } +} \ No newline at end of file diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.fsh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.fsh new file mode 100644 index 00000000..de9b92f1 --- /dev/null +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.fsh @@ -0,0 +1,7 @@ +#version 330 + +out vec4 fragColor; + +void main() { + fragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.vsh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.vsh new file mode 100644 index 00000000..ce45a8af --- /dev/null +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/core/occlusion_query.vsh @@ -0,0 +1,12 @@ +#version 330 + +layout(std140) uniform Transforms { + mat4 ProjMat; + mat4 ModelViewMat; +}; + +in vec3 Position; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); +} From fb6fea082ac372492e08b4deee8588fbc8099aea Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Tue, 11 Aug 2026 18:09:36 +0800 Subject: [PATCH 3/6] feat(rendering): migrate GL calls in ALRComputeCapabilities to ALRHICapabilities --- .../blaze3d/ALRGpuDeviceBackendExtension.java | 2 + .../blaze3d/ALRGpuDeviceExtension.java | 2 + .../extension/blaze3d/ALRHICapabilities.java | 12 +++ .../compute/ALRComputeCapabilities.java | 4 +- .../shader/ALRComputeShaderManager.java | 1 - .../v2/rendering/mixins/MinecraftMixin.java | 2 + .../mixins/blaze3d/GpuDeviceMixin.java | 6 ++ .../mixins/blaze3d/gl/GlDeviceMixin.java | 18 ++++ .../shaders/compute/hiz.csh | 100 ++++++++++++++++++ .../test/client/compute/ComputeSupport.java | 4 +- 10 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHICapabilities.java create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/hiz.csh diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java index f2c59eb4..a268bb61 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java @@ -19,4 +19,6 @@ public interface ALRGpuDeviceBackendExtension { void alrPopDebugGroup(); GpuQueryObject alrCreateSamplesQuery(); + + ALRHICapabilities alrhiCreateCapabilities(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java index 7d1e80eb..84e31837 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java @@ -16,4 +16,6 @@ public interface ALRGpuDeviceExtension { void alrPushDebugGroup(Supplier message); void alrPopDebugGroup(); + + ALRHICapabilities alrhiCreateCapabilities(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHICapabilities.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHICapabilities.java new file mode 100644 index 00000000..61a2814d --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHICapabilities.java @@ -0,0 +1,12 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; + +import com.mojang.blaze3d.systems.RenderSystem; + +public record ALRHICapabilities( + boolean compute +) { + + public static ALRHICapabilities getInstance() { + return ((ALRGpuDeviceExtension) RenderSystem.getDevice()).alrhiCreateCapabilities(); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/ALRComputeCapabilities.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/ALRComputeCapabilities.java index ab178600..49f34c9f 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/ALRComputeCapabilities.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/ALRComputeCapabilities.java @@ -1,12 +1,12 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute; -import org.lwjgl.opengl.GL; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRHICapabilities; public class ALRComputeCapabilities { private static boolean COMPUTE_SUPPORTED; public static void init() { - COMPUTE_SUPPORTED = GL.getCapabilities().GL_ARB_compute_shader; + COMPUTE_SUPPORTED = ALRHICapabilities.getInstance().compute(); } public static boolean isComputeSupported() { diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java index 7dd02580..22faeebc 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeShaderManager.java @@ -62,7 +62,6 @@ public class ALRComputeShaderManager extends SimplePreparableReloadListener> 2], (int(id) & 3) * 8, 8); +} + +layout(location = 0) uniform vec2 invImSize; + +layout(binding = 0) uniform sampler2D mip_0; +layout(binding = 1, r32f) uniform restrict writeonly image2D mip_1; +layout(binding = 2, r32f) uniform restrict writeonly image2D mip_2; +layout(binding = 3, r32f) uniform restrict writeonly image2D mip_3; +layout(binding = 4, r32f) uniform restrict writeonly image2D mip_4; +layout(binding = 5, r32f) uniform restrict writeonly image2D mip_5; +layout(binding = 6, r32f) uniform restrict writeonly image2D mip_6; + +float getReduce2x2(ivec2 pos) { // w.r.t mip_1 + vec4 data = textureGather(mip_0, vec2(pos * 2 + 1) * invImSize); + float ret = max(max(data.x, data.y), max(data.z, data.w)); + imageStore(mip_1, pos, vec4(ret)); + return ret; +} + +float getReduce4x4(ivec2 pos) { // w.r.t mip_2 + ivec2 pos2 = pos * 2; + float ret = max( + max(getReduce2x2(pos2 + ivec2(0, 0)), getReduce2x2(pos2 + ivec2(0, 1))), + max(getReduce2x2(pos2 + ivec2(1, 0)), getReduce2x2(pos2 + ivec2(1, 1))) + ); + imageStore(mip_2, pos, vec4(ret)); + return ret; +} + +// This is where the funny happens +// since we swizzeled the id when getting the value, our ordering within the subgroup should be z ordered +// we sadly cannot use the full subgroup reduction as wave size is 32 and we need a square pow2 values, so 16, sad beep +float getReduceWave(ivec2 pos, float value) { + float reduced; + subgroupBarrier(); // Wait for active threads in subgroup + // Now do clustered reduction, with exploiting dropout + reduced = subgroupClusteredMax(value, 4); + if ((gl_SubgroupInvocationID & 0x3) == 0) { // root writes + imageStore(mip_3, pos >> 1, vec4(reduced)); + } + // could exit 3/4 of the threads here if wanted + subgroupBarrier(); // Wait for active threads in subgroup + reduced = subgroupClusteredMax(value, 16); + if ((gl_SubgroupInvocationID & 0xF) == 0) { // root writes + imageStore(mip_4, pos >> 2, vec4(reduced)); + } + return reduced; +} + +shared float values[16]; + +void main() { + uint id = swizzleId(gl_LocalInvocationID.x); + //(ivec2(gl_WorkGroupID.xy) * 64 + ivec2(id & 0xFU, id >> 4) * 4) / 4; + ivec2 wavePos = ivec2(gl_WorkGroupID.xy) * 16 + ivec2(id & 0xFU, id >> 4); + float value = getReduce4x4(wavePos); + value = getReduceWave(wavePos, value); // Reduced to 4x4 across all threads and warps + if ((gl_LocalInvocationID.x & 0xFU) == 0) { + values[gl_LocalInvocationID.x >> 4] = value; + } + barrier(); // Wait for all + if ((gl_LocalInvocationID.x >> 2) != 0) { + return; // Discard all but 4 threads + } + uint i = gl_LocalInvocationID.x * 4; + value = max(max(values[i], values[i + 1]), max(values[i + 2], values[i + 3])); // Is funny is already in spread order + imageStore( + mip_5, + ivec2(gl_WorkGroupID.xy) * 2 + ivec2(gl_LocalInvocationID.x & 1u, gl_LocalInvocationID.x >> 1), + vec4(value) + ); + subgroupBarrier(); + value = subgroupMax(value); + if (gl_LocalInvocationID.x == 0) { + imageStore(mip_6, ivec2(gl_WorkGroupID.xy), vec4(value)); + } +} diff --git a/module.test/src/main/java/dev/anvilcraft/lib/v2/test/client/compute/ComputeSupport.java b/module.test/src/main/java/dev/anvilcraft/lib/v2/test/client/compute/ComputeSupport.java index 47a92e29..1a8b6614 100644 --- a/module.test/src/main/java/dev/anvilcraft/lib/v2/test/client/compute/ComputeSupport.java +++ b/module.test/src/main/java/dev/anvilcraft/lib/v2/test/client/compute/ComputeSupport.java @@ -31,7 +31,7 @@ import java.util.OptionalDouble; public class ComputeSupport { - public static final ComputeSupport INSTANCE = new ComputeSupport(); + public static final ComputeSupport INSTANCE = ALRComputeCapabilities.isComputeSupported() ? new ComputeSupport() : null; public static final float[] UNSUPPORTED = {}; @Getter private final GpuDevice device = RenderSystem.getDevice(); @@ -144,7 +144,7 @@ public float[] add(float[] input, float f) { ByteBuffer counterData = mappedCounterBuffer.data(); int anInt = counterData.getInt(); - if (anInt != input.length){ + if (anInt != input.length) { System.out.printf("Compute counter does not match with input size: %d/%d%n", anInt, input.length); } return result; From 8f2358fd338425b6ad47b2c61e3de54778e0ea23 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 12 Aug 2026 05:54:17 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat(opts-hiz-occlusion):=20=E6=84=9F?= =?UTF-8?q?=E8=B0=A2amd=E5=BC=80=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/v2/rendering/ALRComputePipelines.java | 31 + .../lib/v2/rendering/ALROptions.java | 11 + .../object/BufferObjectLayoutEntry.java | 8 + .../buffers/ubo/FullTransformsUbo.java | 6 + .../foundation/buffers/ubo/IntSizeUbo.java | 41 ++ .../occlusion/hiz/ConvertDepthParamsUbo.java | 44 ++ .../occlusion/hiz/SPDConstantBuffer.java | 46 ++ .../shaders/compute/depth_convert.csh | 26 + .../compute/ffx_spd_downsample_pass.csh | 679 ++++++++++++++++++ .../shaders/compute/hiz.csh | 100 --- .../shaders/include/tonemappers.glsl | 159 ++++ 11 files changed, 1051 insertions(+), 100 deletions(-) create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptions.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/IntSizeUbo.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh delete mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/hiz.csh create mode 100644 module.rendering/src/main/resources/assets/anvillib_rendering/shaders/include/tonemappers.glsl diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java new file mode 100644 index 00000000..afdd465b --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java @@ -0,0 +1,31 @@ +package dev.anvilcraft.lib.v2.rendering; + +import dev.anvilcraft.lib.v2.rendering.event.RegisterComputePipelinesEvent; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePipeline; +import net.minecraft.client.renderer.ShaderDefines; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; + +@EventBusSubscriber(Dist.CLIENT) +public class ALRComputePipelines { + public static final ALRComputePipeline FFX_SPD_DOWNSAMPLE_PASS = ALRComputePipeline.builder() + .withName(AnvilLibRendering.location("ffx_spd_downsample_pass")) + .withShader(AnvilLibRendering.location("compute/ffx_spd_downsample_pass.csh")) + .withDefines( + ShaderDefines.builder() + .define("FFX_SPD_OPTION_DOWNSAMPLE_FILTER", "2") // use max for HZB + .define("FFX_SPD_OPTION_WAVE_INTEROP_LDS", ALROptions.SPD_OPTION_WAVE_INTEROP_LDS ? 0 : 1) // weird reverted + .build() + ) + .withTexture("r_input_downsample_src") // a sampler2DArray in shader, changes needed? + .withReadWriteImage("rw_input_downsample_src_mid_mip") // a image2DArray in shader, changes needed? + .withReadWriteImage("rw_input_downsample_src_mips") // an array of image2DArray in shader, changes needed? length=13 + .build(); + + + @SubscribeEvent + public static void on(RegisterComputePipelinesEvent event) { + event.registerPipeline(FFX_SPD_DOWNSAMPLE_PASS); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptions.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptions.java new file mode 100644 index 00000000..f6c44f59 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptions.java @@ -0,0 +1,11 @@ +package dev.anvilcraft.lib.v2.rendering; + +public class ALROptions { + public static final boolean SPD_OPTION_WAVE_INTEROP_LDS = getPropertyBoolean("alrSpdOptionUseWaveInteropLds"); + + + private static boolean getPropertyBoolean(String key) { + String prop = System.getProperty(key); + return !"false".equals(prop); + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObjectLayoutEntry.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObjectLayoutEntry.java index c44fad34..b0e671f8 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObjectLayoutEntry.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObjectLayoutEntry.java @@ -11,6 +11,8 @@ import org.joml.Vector4f; import org.joml.Vector4i; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; public record BufferObjectLayoutEntry(BufferObjectLayoutEntryType type, Function getter) { @@ -70,6 +72,7 @@ public static Builder ofMat4f() { public static class Builder { private final BufferObjectLayoutEntryType type; private Function getter; + private BiConsumer setter; public Builder(BufferObjectLayoutEntryType type) { this.type = type; @@ -80,6 +83,11 @@ public Builder forGetter(Function getter) { return this; } + public Builder forSetter(BiConsumer setter) { + this.setter = setter; + return this; + } + public BufferObjectLayoutEntry build() { return new BufferObjectLayoutEntry<>(type, getter); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java index c6edcc82..37d434ac 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/FullTransformsUbo.java @@ -10,6 +10,12 @@ import org.jetbrains.annotations.ApiStatus; import org.joml.Matrix4f; +/// ```glsl +/// layout(std140) uniform Transforms { +/// mat4 ProjMat; +/// mat4 ModelViewMat; +/// }; +/// ``` @Setter @Getter @ApiStatus.Internal diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/IntSizeUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/IntSizeUbo.java new file mode 100644 index 00000000..d96fbfdd --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/ubo/IntSizeUbo.java @@ -0,0 +1,41 @@ +package dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo; + +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutDefinition; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutEntry; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; + +/// ```glsl +/// layout(std140, binding = 0) uniform SizeParam { +/// int uWidth; +/// int uHeight; +/// }; +/// ``` +@Getter +@Setter +@ApiStatus.Internal +public class IntSizeUbo extends BufferObject { + + public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( + BufferObjectLayoutEntry.ofInt().forGetter(IntSizeUbo::getHeight).build(), + BufferObjectLayoutEntry.ofInt().forGetter(IntSizeUbo::getWidth).build() + ); + + public static final int SIZE = DEFINITION.size(BufferLayout.STD140); + + private int width; + private int height; + + protected IntSizeUbo() { + super(BufferLayout.STD140, ShaderBufferObjectUsage.UBO); + } + + @Override + protected BufferObjectLayoutDefinition getDefinition() { + return DEFINITION; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java new file mode 100644 index 00000000..181f119d --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java @@ -0,0 +1,44 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; + +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutDefinition; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutEntry; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; + +/// ```glsl +/// layout(std140, binding = 0) uniform ConvertParam { +/// int uWidth; +/// int uHeight; +/// float uPadValue; +/// }; +/// ``` +@Getter +@Setter +@ApiStatus.Internal +public class ConvertDepthParamsUbo extends BufferObject { + + public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( + BufferObjectLayoutEntry.ofInt().forGetter(ConvertDepthParamsUbo::getHeight).build(), + BufferObjectLayoutEntry.ofInt().forGetter(ConvertDepthParamsUbo::getWidth).build(), + BufferObjectLayoutEntry.ofFloat().forGetter(ConvertDepthParamsUbo::getPadValue).build() + ); + + public static final int SIZE = DEFINITION.size(BufferLayout.STD140); + + private int width; + private int height; + private float padValue = 1f; + + protected ConvertDepthParamsUbo() { + super(BufferLayout.STD140, ShaderBufferObjectUsage.UBO); + } + + @Override + protected BufferObjectLayoutDefinition getDefinition() { + return DEFINITION; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java new file mode 100644 index 00000000..1ca96812 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java @@ -0,0 +1,46 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; + +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutDefinition; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutEntry; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; +import lombok.Getter; +import lombok.Setter; +import org.joml.Vector2f; + +@Getter +@Setter +public class SPDConstantBuffer extends BufferObject { + + public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( + BufferObjectLayoutEntry.ofInt().forGetter(SPDConstantBuffer::getMips).build(), + BufferObjectLayoutEntry.ofInt().forGetter(SPDConstantBuffer::getNumWorkGroups).build(), + BufferObjectLayoutEntry.ofVec2f().forGetter(SPDConstantBuffer::getWorkGroupOffset).build(), + BufferObjectLayoutEntry.ofVec2f().forGetter(SPDConstantBuffer::getInvInputSize).build() + ); + + /// The total number of mip levels SPD generates for each input texture slice. + private int mips = 13; + + /// The total number of dispatched work groups for one texture-array slice. SPD uses this to identify the last + /// work group, which downsamples the remaining mip levels. + private int numWorkGroups; + + /// The offset of the first 64x64 input tile in work-group coordinates, normally `(left / 64, top / 64)` for a + /// downsampled subregion. + private Vector2f workGroupOffset; + + /// The input texture size is `size = (width, height)`. This field stores + /// `invInputSize = (1.0 / size.x, 1.0 / size.y)` for normalized UV conversion when linear sampling is enabled. + private Vector2f invInputSize; + + protected SPDConstantBuffer() { + super(BufferLayout.STD140, ShaderBufferObjectUsage.UBO); + } + + @Override + protected BufferObjectLayoutDefinition getDefinition() { + return DEFINITION; + } +} diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh new file mode 100644 index 00000000..3d7dbaab --- /dev/null +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh @@ -0,0 +1,26 @@ +#version 460 core + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +layout(std140, binding = 0) uniform ConvertParam { + int uWidth; + int uHeight; + float uPadValue; +}; + +layout(binding = 1) uniform sampler2D Input; + +layout(binding = 2, r32f) writeonly uniform image2D Output; + +void main() { + ivec2 idx = ivec2(gl_GlobalInvocationID.xy); + + if (idx.x >= uWidth || idx.y >= uHeight) { + imageStore(Output, idx, vec4(1, 1, 1, 1)); + return; + } + + vec4 converted = texelFetch(Input, idx, 0); + + imageStore(Output, idx, converted); +} \ No newline at end of file diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh new file mode 100644 index 00000000..295a3197 --- /dev/null +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh @@ -0,0 +1,679 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// SPD pass +// SRV 0 : SPD_InputDownsampleSrc : r_input_downsample_src +// UAV 0 : SPD_InternalGlobalAtomic : rw_internal_global_atomic +// UAV 1 : SPD_InputDownsampleSrcMidMip : rw_input_downsample_src_mid_mip +// UAV 2 : SPD_InputDownsampleSrcMips : rw_input_downsample_src_mips +// CB 0 : cbSPD + +#version 450 + +#define SPD_MAX_MIP_LEVELS 12 + +#ifndef FFX_SPD_OPTION_DOWNSAMPLE_FILTER +#define FFX_SPD_OPTION_DOWNSAMPLE_FILTER 0 +#endif + +#ifndef FFX_SPD_OPTION_LINEAR_SAMPLE +#define FFX_SPD_OPTION_LINEAR_SAMPLE 0 +#endif + +#ifndef FFX_SPD_OPTION_WAVE_INTEROP_LDS +#define FFX_SPD_OPTION_WAVE_INTEROP_LDS 0 +#endif + +#if FFX_SPD_OPTION_LINEAR_SAMPLE +#define SPD_LINEAR_SAMPLER 1 +#endif + +#if FFX_SPD_OPTION_WAVE_INTEROP_LDS +#define FFX_SPD_NO_WAVE_OPERATIONS 1 +#else +#extension GL_KHR_shader_subgroup_quad : require +#endif + + +layout (set = 0, binding = 0, std140) uniform cbFSR1_t +{ + uint mips; + uint numWorkGroups; + uvec2 workGroupOffset; + vec2 invInputSize; // Only used for linear sampling mode +} cbFSR1; + +uint Mips() +{ + return cbFSR1.mips; +} + +uint NumWorkGroups() +{ + return cbFSR1.numWorkGroups; +} + +uvec2 WorkGroupOffset() +{ + return cbFSR1.workGroupOffset; +} + +vec2 InvInputSize() +{ + return cbFSR1.invInputSize; +} + +// separate texture and sampler objects are unavailable in opengl +//layout (set = 0, binding = 1000) uniform sampler s_LinearClamp; +//// SRVs +//layout (set = 0, binding = 0) uniform texture2DArray r_input_downsample_src; + +layout (set = 0, binding = 1) uniform sampler2DArray r_input_downsample_src; + +// UAV declarations +// replace huge binding slot in original shader 2000 to 2 +layout (set = 0, binding = 2, std430) coherent buffer rw_internal_global_atomic_t +{ + uint counter[6]; +} rw_internal_global_atomic; + +// replace huge binding slot in original shader 2001 to 3 +// bind mip map 6 to this uniform +// change format from rgba32f to r32f because we are handling depth texture +layout (set = 0, binding = 3, r32f) coherent uniform image2DArray rw_input_downsample_src_mid_mip; + +// replace huge binding slot in original shader 2002 to 4 +// change format from rgba32f to r32f because we are handling depth texture +layout (set = 0, binding = 4, r32f) uniform image2DArray rw_input_downsample_src_mips[SPD_MAX_MIP_LEVELS + 1]; + +/// Compute an SRGB value from a linear value. +/// +/// @param [in] value The value to convert to SRGB from linear. +/// +/// @returns +/// A value in SRGB space. +/// +/// @ingroup GPUCore +float ffxSrgbFromLinear(float value) +{ + vec3 j = vec3(0.0031308 * 12.92, 12.92, 1.0 / 2.4); + vec2 k = vec2(1.055, -0.055); + // wrong clamp order? original: + // return clamp(j.x, value * j.y, pow(value, j.z) * k.x + k.y); + return clamp(pow(value, j.z) * k.x + k.y, j.x, value * j.y); +} + +/// A helper function performing a remap 64x1 to 8x8 remapping which is necessary for 2D wave reductions. +/// +/// The 64-wide lane indices to 8x8 remapping is performed as follows: +/// +/// 00 01 08 09 10 11 18 19 +/// 02 03 0a 0b 12 13 1a 1b +/// 04 05 0c 0d 14 15 1c 1d +/// 06 07 0e 0f 16 17 1e 1f +/// 20 21 28 29 30 31 38 39 +/// 22 23 2a 2b 32 33 3a 3b +/// 24 25 2c 2d 34 35 3c 3d +/// 26 27 2e 2f 36 37 3e 3f +/// +/// @param [in] a The input 1D coordinate to remap. +/// +/// @returns +/// The remapped 2D coordinates. +/// +/// @ingroup GPUCore +uvec2 ffxRemapForWaveReduction(uint a) +{ + return uvec2(((a >> 2u) & 6u) | (a & 1u), ((a >> 3u) & 4u) | ((a >> 1u) & 3u)); +} + +vec4 SampleSrcImage(ivec2 uv, uint slice) +{ + vec2 textureCoord = vec2(uv) * InvInputSize() + InvInputSize(); + // vec4 result = textureLod(sampler2DArray(r_input_downsample_src, s_LinearClamp), vec3(textureCoord, slice), 0); + vec4 result = textureLod(r_input_downsample_src, vec3(textureCoord, slice), 0); + // remove srgb convert because minecraft use linear rgb8 unorm + // return vec4(ffxSrgbFromLinear(result.x), ffxSrgbFromLinear(result.y), ffxSrgbFromLinear(result.z), result.w); + return result; +} + +vec4 LoadSrcImage(ivec2 uv, uint slice) +{ + return imageLoad(rw_input_downsample_src_mips[0], ivec3(uv, slice)); +} + +void StoreSrcMip(vec4 value, ivec2 uv, uint slice, uint mip) +{ + imageStore(rw_input_downsample_src_mips[mip], ivec3(uv, slice), value); +} + +vec4 LoadMidMip(ivec2 uv, uint slice) +{ + return imageLoad(rw_input_downsample_src_mid_mip, ivec3(uv, slice)); +} + +void StoreMidMip(vec4 value, ivec2 uv, uint slice) +{ + imageStore(rw_input_downsample_src_mid_mip, ivec3(uv, slice), value); +} + +void IncreaseAtomicCounter(uint slice, inout uint counter) +{ + counter = atomicAdd(rw_internal_global_atomic.counter[slice], 1); +} + +void ResetAtomicCounter(uint slice) +{ + rw_internal_global_atomic.counter[slice] = 0; +} + +shared uint spdCounter; + +void SpdIncreaseAtomicCounter(uint slice) +{ + IncreaseAtomicCounter(slice, spdCounter); +} + +uint SpdGetAtomicCounter() +{ + return spdCounter; +} + +void SpdResetAtomicCounter(uint slice) +{ + ResetAtomicCounter(slice); +} + +shared float spdIntermediateR[16][16]; +shared float spdIntermediateG[16][16]; +shared float spdIntermediateB[16][16]; +shared float spdIntermediateA[16][16]; + +vec4 SpdLoadSourceImage(ivec2 tex, uint slice) +{ + #if defined SPD_LINEAR_SAMPLER + return SampleSrcImage(tex, slice); + #else + return LoadSrcImage(tex, slice); + #endif // SPD_LINEAR_SAMPLER +} + +vec4 SpdLoad(ivec2 tex, uint slice) +{ + return LoadMidMip(tex, slice); +} + +void SpdStore(ivec2 pix, vec4 outValue, uint mip, uint slice) +{ + if (mip == 5) + StoreMidMip(outValue, pix, slice); + else + StoreSrcMip(outValue, pix, slice, mip + 1); +} + +vec4 SpdLoadIntermediate(uint x, uint y) +{ + return vec4(spdIntermediateR[x][y], spdIntermediateG[x][y], spdIntermediateB[x][y], spdIntermediateA[x][y]); +} + +void SpdStoreIntermediate(uint x, uint y, vec4 value) +{ + spdIntermediateR[x][y] = value.x; + spdIntermediateG[x][y] = value.y; + spdIntermediateB[x][y] = value.z; + spdIntermediateA[x][y] = value.w; +} + +vec4 SpdReduce4(vec4 v0, vec4 v1, vec4 v2, vec4 v3) +{ + #if FFX_SPD_OPTION_DOWNSAMPLE_FILTER == 1 + return min(min(v0, v1), min(v2, v3)); + #elif FFX_SPD_OPTION_DOWNSAMPLE_FILTER == 2 + return max(max(v0, v1), max(v2, v3)); + #else + return (v0 + v1 + v2 + v3) * 0.25; + #endif +} + +void ffxSpdWorkgroupShuffleBarrier() +{ + groupMemoryBarrier(); + barrier(); +} + +// Only last active workgroup should proceed +bool SpdExitWorkgroup(uint numWorkGroups, uint localInvocationIndex, uint slice) +{ + // global atomic counter + if (localInvocationIndex == 0) + { + SpdIncreaseAtomicCounter(slice); + } + + ffxSpdWorkgroupShuffleBarrier(); + return (SpdGetAtomicCounter() != (numWorkGroups - 1)); +} + +// User defined: vec4 SpdReduce4(vec4 v0, vec4 v1, vec4 v2, vec4 v3); +vec4 SpdReduceQuad(vec4 v) +{ + #if !defined(FFX_SPD_NO_WAVE_OPERATIONS) + vec4 v0 = v; + vec4 v1 = subgroupQuadSwapHorizontal(v); + vec4 v2 = subgroupQuadSwapVertical(v); + vec4 v3 = subgroupQuadSwapDiagonal(v); + return SpdReduce4(v0, v1, v2, v3); + #endif + return v; +} + +vec4 SpdReduceIntermediate(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3) +{ + vec4 v0 = SpdLoadIntermediate(i0.x, i0.y); + vec4 v1 = SpdLoadIntermediate(i1.x, i1.y); + vec4 v2 = SpdLoadIntermediate(i2.x, i2.y); + vec4 v3 = SpdLoadIntermediate(i3.x, i3.y); + return SpdReduce4(v0, v1, v2, v3); +} + +vec4 SpdReduceLoad4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3, uint slice) +{ + vec4 v0 = SpdLoad(ivec2(i0), slice); + vec4 v1 = SpdLoad(ivec2(i1), slice); + vec4 v2 = SpdLoad(ivec2(i2), slice); + vec4 v3 = SpdLoad(ivec2(i3), slice); + return SpdReduce4(v0, v1, v2, v3); +} + +vec4 SpdReduceLoad4(uvec2 base, uint slice) +{ + return SpdReduceLoad4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1), slice); +} + +vec4 SpdReduceLoadSourceImage4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3, uint slice) +{ + vec4 v0 = SpdLoadSourceImage(ivec2(i0), slice); + vec4 v1 = SpdLoadSourceImage(ivec2(i1), slice); + vec4 v2 = SpdLoadSourceImage(ivec2(i2), slice); + vec4 v3 = SpdLoadSourceImage(ivec2(i3), slice); + return SpdReduce4(v0, v1, v2, v3); +} + +vec4 SpdReduceLoadSourceImage(uvec2 base, uint slice) +{ + #if defined(SPD_LINEAR_SAMPLER) + return SpdLoadSourceImage(ivec2(base), slice); + #else + return SpdReduceLoadSourceImage4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1), slice); + #endif +} + +void SpdDownsampleMips_0_1_Intrinsics(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + vec4 v[4]; + + ivec2 tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2); + ivec2 pix = ivec2(workGroupID.xy * 32) + ivec2(x, y); + v[0] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[0], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2); + pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y); + v[1] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[1], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2 + 32); + pix = ivec2(workGroupID.xy * 32) + ivec2(x, y + 16); + v[2] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[2], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2 + 32); + pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[3], 0, slice); + + if (mip <= 1) + return; + + v[0] = SpdReduceQuad(v[0]); + v[1] = SpdReduceQuad(v[1]); + v[2] = SpdReduceQuad(v[2]); + v[3] = SpdReduceQuad(v[3]); + + if ((localInvocationIndex % 4) == 0) + { + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2), v[0], 1, slice); + SpdStoreIntermediate(x / 2, y / 2, v[0]); + + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2), v[1], 1, slice); + SpdStoreIntermediate(x / 2 + 8, y / 2, v[1]); + + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2 + 8), v[2], 1, slice); + SpdStoreIntermediate(x / 2, y / 2 + 8, v[2]); + + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); + SpdStoreIntermediate(x / 2 + 8, y / 2 + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1_LDS(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + vec4 v[4]; + + ivec2 tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2); + ivec2 pix = ivec2(workGroupID.xy * 32) + ivec2(x, y); + v[0] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[0], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2); + pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y); + v[1] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[1], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2 + 32); + pix = ivec2(workGroupID.xy * 32) + ivec2(x, y + 16); + v[2] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[2], 0, slice); + + tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2 + 32); + pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y + 16); + v[3] = SpdReduceLoadSourceImage(tex, slice); + SpdStore(pix, v[3], 0, slice); + + if (mip <= 1) + return; + + for (uint i = 0; i < 4; i++) + { + SpdStoreIntermediate(x, y, v[i]); + ffxSpdWorkgroupShuffleBarrier(); + if (localInvocationIndex < 64) + { + v[i] = SpdReduceIntermediate(uvec2(x * 2 + 0, y * 2 + 0), uvec2(x * 2 + 1, y * 2 + 0), uvec2(x * 2 + 0, y * 2 + 1), uvec2(x * 2 + 1, y * 2 + 1)); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); + } + ffxSpdWorkgroupShuffleBarrier(); + } + + if (localInvocationIndex < 64) + { + SpdStoreIntermediate(x + 0, y + 0, v[0]); + SpdStoreIntermediate(x + 8, y + 0, v[1]); + SpdStoreIntermediate(x + 0, y + 8, v[2]); + SpdStoreIntermediate(x + 8, y + 8, v[3]); + } +} + +void SpdDownsampleMips_0_1(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + #if defined(FFX_SPD_NO_WAVE_OPERATIONS) + SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip, slice); + #else + SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip, slice); + #endif +} + +void SpdDownsampleMip_2(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + #if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 64) + { + vec4 v = SpdReduceIntermediate(uvec2(x * 2 + 0, y * 2 + 0), uvec2(x * 2 + 1, y * 2 + 0), uvec2(x * 2 + 0, y * 2 + 1), uvec2(x * 2 + 1, y * 2 + 1)); + SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x, y), v, mip, slice); + // store to LDS, try to reduce bank conflicts + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 x + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + // ... + // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 + SpdStoreIntermediate(x * 2 + y % 2, y * 2, v); + } + #else + vec4 v = SpdLoadIntermediate(x, y); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x + (y / 2) % 2, y, v); + } + #endif +} + +void SpdDownsampleMip_3(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + #if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 16) + { + // x 0 x 0 + // 0 0 0 0 + // 0 x 0 x + // 0 0 0 0 + vec4 v = SpdReduceIntermediate(uvec2(x * 4 + 0 + 0, y * 4 + 0), uvec2(x * 4 + 2 + 0, y * 4 + 0), uvec2(x * 4 + 0 + 1, y * 4 + 2), uvec2(x * 4 + 2 + 1, y * 4 + 2)); + SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x, y), v, mip, slice); + // store to LDS + // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 + // ... + // 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x 0 + // ... + // 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 x + // ... + SpdStoreIntermediate(x * 4 + y, y * 4, v); + } + #else + if (localInvocationIndex < 64) + { + vec4 v = SpdLoadIntermediate(x * 2 + y % 2, y * 2); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x * 2 + y / 2, y * 2, v); + } + } + #endif +} + +void SpdDownsampleMip_4(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + #if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 4) + { + // x 0 0 0 x 0 0 0 + // ... + // 0 x 0 0 0 x 0 0 + vec4 v = SpdReduceIntermediate(uvec2(x * 8 + 0 + 0 + y * 2, y * 8 + 0), + uvec2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), + uvec2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), + uvec2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); + SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x, y), v, mip, slice); + // store to LDS + // x x x x 0 ... + // 0 ... + SpdStoreIntermediate(x + y * 2, 0, v); + } + #else + if (localInvocationIndex < 16) + { + vec4 v = SpdLoadIntermediate(x * 4 + y, y * 4); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStoreIntermediate(x / 2 + y, 0, v); + } + } + #endif +} + +void SpdDownsampleMip_5(uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +{ + #if defined(FFX_SPD_NO_WAVE_OPERATIONS) + if (localInvocationIndex < 1) + { + // x x x x 0 ... + // 0 ... + vec4 v = SpdReduceIntermediate(uvec2(0, 0), uvec2(1, 0), uvec2(2, 0), uvec2(3, 0)); + SpdStore(ivec2(workGroupID.xy), v, mip, slice); + } + #else + if (localInvocationIndex < 4) + { + vec4 v = SpdLoadIntermediate(localInvocationIndex, 0); + v = SpdReduceQuad(v); + // quad index 0 stores result + if (localInvocationIndex % 4 == 0) + { + SpdStore(ivec2(workGroupID.xy), v, mip, slice); + } + } + #endif +} + +void SpdDownsampleMips_6_7(uint x, uint y, uint mips, uint slice) +{ + ivec2 tex = ivec2(x * 4 + 0, y * 4 + 0); + ivec2 pix = ivec2(x * 2 + 0, y * 2 + 0); + vec4 v0 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v0, 6, slice); + + tex = ivec2(x * 4 + 2, y * 4 + 0); + pix = ivec2(x * 2 + 1, y * 2 + 0); + vec4 v1 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v1, 6, slice); + + tex = ivec2(x * 4 + 0, y * 4 + 2); + pix = ivec2(x * 2 + 0, y * 2 + 1); + vec4 v2 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v2, 6, slice); + + tex = ivec2(x * 4 + 2, y * 4 + 2); + pix = ivec2(x * 2 + 1, y * 2 + 1); + vec4 v3 = SpdReduceLoad4(tex, slice); + SpdStore(pix, v3, 6, slice); + + if (mips <= 7) + return; + // no barrier needed, working on values only from the same thread + + vec4 v = SpdReduce4(v0, v1, v2, v3); + SpdStore(ivec2(x, y), v, 7, slice); + SpdStoreIntermediate(x, y, v); +} + +void SpdDownsampleNextFour(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint baseMip, uint mips, uint slice) +{ + if (mips <= baseMip) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip, slice); + + if (mips <= baseMip + 1) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); + + if (mips <= baseMip + 2) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); + + if (mips <= baseMip + 3) + return; + ffxSpdWorkgroupShuffleBarrier(); + SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3, slice); +} + +/// Downsamples a 64x64 tile based on the work group id. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// +/// @ingroup FfxGPUSpd +void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups, uint slice) +{ + // compute MIP level 0 and 1 + uvec2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); + uint x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); + uint y = sub_xy.y + 8 * (localInvocationIndex >> 7); + SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips, slice); + + // compute MIP level 2, 3, 4, 5 + SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips, slice); + + if (mips <= 6) + return; + + // increase the global atomic counter for the given slice and check if it's the last remaining thread group: + // terminate if not, continue if yes. + if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) + return; + + // reset the global atomic counter back to 0 for the next spd dispatch + SpdResetAtomicCounter(slice); + + // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. + // compute MIP level 6 and 7 + SpdDownsampleMips_6_7(x, y, mips, slice); + + // compute MIP level 8, 9, 10, 11 + SpdDownsampleNextFour(x, y, uvec2(0, 0), localInvocationIndex, 8, mips, slice); +} + +/// Downsamples a 64x64 tile based on the work group id and work group offset. +/// If after downsampling it's the last active thread group, computes the remaining MIP levels. +/// +/// @param [in] workGroupID index of the work group / thread group +/// @param [in] localInvocationIndex index of the thread within the thread group in 1D +/// @param [in] mips the number of total MIP levels to compute for the input texture +/// @param [in] numWorkGroups the total number of dispatched work groups / thread groups for this slice +/// @param [in] slice the slice of the input texture +/// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. +/// +/// @ingroup FfxGPUSpd +void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups, uint slice, uvec2 workGroupOffset) +{ + SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); +} + +void DOWNSAMPLE(uint localThreadId, uvec3 workGroupId) +{ + SpdDownsample(workGroupId.xy, localThreadId, Mips(), NumWorkGroups(), workGroupId.z, WorkGroupOffset()); +} + +layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in; +void main() +{ + DOWNSAMPLE(gl_LocalInvocationIndex, gl_WorkGroupID.xyz); +} diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/hiz.csh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/hiz.csh deleted file mode 100644 index 16529561..00000000 --- a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/hiz.csh +++ /dev/null @@ -1,100 +0,0 @@ -#version 460 core - -#extension GL_KHR_shader_subgroup_arithmetic : require -#extension GL_KHR_shader_subgroup_basic : require -#extension GL_KHR_shader_subgroup_clustered : require - -// 64x64 reduction -layout(local_size_x = 256) in; - -const uint spread[64] = { - 0x11100100, 0x13120302, 0x31302120, 0x33322322, 0x15140504, 0x17160706, 0x35342524, 0x37362726, - 0x51504140, 0x53524342, 0x71706160, 0x73726362, 0x55544544, 0x57564746, 0x75746564, 0x77766766, - 0x19180908, 0x1b1a0b0a, 0x39382928, 0x3b3a2b2a, 0x1d1c0d0c, 0x1f1e0f0e, 0x3d3c2d2c, 0x3f3e2f2e, - 0x59584948, 0x5b5a4b4a, 0x79786968, 0x7b7a6b6a, 0x5d5c4d4c, 0x5f5e4f4e, 0x7d7c6d6c, 0x7f7e6f6e, - 0x91908180, 0x93928382, 0xb1b0a1a0, 0xb3b2a3a2, 0x95948584, 0x97968786, 0xb5b4a5a4, 0xb7b6a7a6, - 0xd1d0c1c0, 0xd3d2c3c2, 0xf1f0e1e0, 0xf3f2e3e2, 0xd5d4c5c4, 0xd7d6c7c6, 0xf5f4e5e4, 0xf7f6e7e6, - 0x99988988, 0x9b9a8b8a, 0xb9b8a9a8, 0xbbbaabaa, 0x9d9c8d8c, 0x9f9e8f8e, 0xbdbcadac, 0xbfbeafae, - 0xd9d8c9c8, 0xdbdacbca, 0xf9f8e9e8, 0xfbfaebea, 0xdddccdcc, 0xdfdecfce, 0xfdfcedec, 0xfffeefee -}; - -uint swizzleId(uint id) { - // swizzel to z curve - return bitfieldExtract(spread[id >> 2], (int(id) & 3) * 8, 8); -} - -layout(location = 0) uniform vec2 invImSize; - -layout(binding = 0) uniform sampler2D mip_0; -layout(binding = 1, r32f) uniform restrict writeonly image2D mip_1; -layout(binding = 2, r32f) uniform restrict writeonly image2D mip_2; -layout(binding = 3, r32f) uniform restrict writeonly image2D mip_3; -layout(binding = 4, r32f) uniform restrict writeonly image2D mip_4; -layout(binding = 5, r32f) uniform restrict writeonly image2D mip_5; -layout(binding = 6, r32f) uniform restrict writeonly image2D mip_6; - -float getReduce2x2(ivec2 pos) { // w.r.t mip_1 - vec4 data = textureGather(mip_0, vec2(pos * 2 + 1) * invImSize); - float ret = max(max(data.x, data.y), max(data.z, data.w)); - imageStore(mip_1, pos, vec4(ret)); - return ret; -} - -float getReduce4x4(ivec2 pos) { // w.r.t mip_2 - ivec2 pos2 = pos * 2; - float ret = max( - max(getReduce2x2(pos2 + ivec2(0, 0)), getReduce2x2(pos2 + ivec2(0, 1))), - max(getReduce2x2(pos2 + ivec2(1, 0)), getReduce2x2(pos2 + ivec2(1, 1))) - ); - imageStore(mip_2, pos, vec4(ret)); - return ret; -} - -// This is where the funny happens -// since we swizzeled the id when getting the value, our ordering within the subgroup should be z ordered -// we sadly cannot use the full subgroup reduction as wave size is 32 and we need a square pow2 values, so 16, sad beep -float getReduceWave(ivec2 pos, float value) { - float reduced; - subgroupBarrier(); // Wait for active threads in subgroup - // Now do clustered reduction, with exploiting dropout - reduced = subgroupClusteredMax(value, 4); - if ((gl_SubgroupInvocationID & 0x3) == 0) { // root writes - imageStore(mip_3, pos >> 1, vec4(reduced)); - } - // could exit 3/4 of the threads here if wanted - subgroupBarrier(); // Wait for active threads in subgroup - reduced = subgroupClusteredMax(value, 16); - if ((gl_SubgroupInvocationID & 0xF) == 0) { // root writes - imageStore(mip_4, pos >> 2, vec4(reduced)); - } - return reduced; -} - -shared float values[16]; - -void main() { - uint id = swizzleId(gl_LocalInvocationID.x); - //(ivec2(gl_WorkGroupID.xy) * 64 + ivec2(id & 0xFU, id >> 4) * 4) / 4; - ivec2 wavePos = ivec2(gl_WorkGroupID.xy) * 16 + ivec2(id & 0xFU, id >> 4); - float value = getReduce4x4(wavePos); - value = getReduceWave(wavePos, value); // Reduced to 4x4 across all threads and warps - if ((gl_LocalInvocationID.x & 0xFU) == 0) { - values[gl_LocalInvocationID.x >> 4] = value; - } - barrier(); // Wait for all - if ((gl_LocalInvocationID.x >> 2) != 0) { - return; // Discard all but 4 threads - } - uint i = gl_LocalInvocationID.x * 4; - value = max(max(values[i], values[i + 1]), max(values[i + 2], values[i + 3])); // Is funny is already in spread order - imageStore( - mip_5, - ivec2(gl_WorkGroupID.xy) * 2 + ivec2(gl_LocalInvocationID.x & 1u, gl_LocalInvocationID.x >> 1), - vec4(value) - ); - subgroupBarrier(); - value = subgroupMax(value); - if (gl_LocalInvocationID.x == 0) { - imageStore(mip_6, ivec2(gl_WorkGroupID.xy), vec4(value)); - } -} diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/include/tonemappers.glsl b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/include/tonemappers.glsl new file mode 100644 index 00000000..d7869d50 --- /dev/null +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/include/tonemappers.glsl @@ -0,0 +1,159 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//-------------------------------------------------------------------------------------- +// Timothy Lottes tone mapper +//-------------------------------------------------------------------------------------- +// General tonemapping operator, build 'b' term. +float ColToneB(float hdrMax, float contrast, float shoulder, float midIn, float midOut) +{ + return + -((-pow(midIn, contrast) + (midOut * (pow(hdrMax, contrast * shoulder) * pow(midIn, contrast) - + pow(hdrMax, contrast) * pow(midIn, contrast * shoulder) * midOut)) / + (pow(hdrMax, contrast * shoulder) * midOut - pow(midIn, contrast * shoulder) * midOut)) / + (pow(midIn, contrast * shoulder) * midOut)); +} + +// General tonemapping operator, build 'c' term. +float ColToneC(float hdrMax, float contrast, float shoulder, float midIn, float midOut) +{ + return (pow(hdrMax, contrast * shoulder) * pow(midIn, contrast) - pow(hdrMax, contrast) * pow(midIn, contrast * shoulder) * midOut) / + (pow(hdrMax, contrast * shoulder) * midOut - pow(midIn, contrast * shoulder) * midOut); +} + +// General tonemapping operator, p := {contrast, shoulder, b, c}. +float ColTone(float x, vec4 p) +{ + float z = pow(x, p.r); + return z / (pow(z, p.g) * p.b + p.a); +} + +vec3 TimothyTonemapper(vec3 color) +{ + const float hdrMax = 16.0; // How much HDR range before clipping. HDR modes likely need this pushed up to say 25.0. + const float contrast = 2.0; // Use as a baseline to tune the amount of contrast the tonemapper has. + const float shoulder = 1.0; // Likely don't need to mess with this factor, unless matching existing tonemapper is not working well. + const float midIn = 0.18; // Most games will have a {0.0 to 1.0} range for LDR so midIn should be 0.18. + const float midOut = 0.18; // Use for LDR. For HDR10 10:10:10:2 use maybe 0.18 / 25.0 to start. For scRGB, this needs recalculating. + + float b = ColToneB(hdrMax, contrast, shoulder, midIn, midOut); + float c = ColToneC(hdrMax, contrast, shoulder, midIn, midOut); + + const float eps = 1e-6; + float peak = max(color.r, max(color.g, color.b)); + peak = max(eps, peak); + + vec3 ratio = color / peak; + peak = ColTone(peak, vec4(contrast, shoulder, b, c)); + + // Probably want these pre-computed and passed over as constants. + const float crosstalk = 4.0; // Controls amount of channel crosstalk. + const float saturation = contrast; // Full tonal range saturation control. + const float crossSaturation = contrast * 16.0; // Crosstalk saturation. + const float white = 1.0; + + // Wrap crosstalk in transform. + ratio = pow(abs(ratio), vec3(saturation / crossSaturation)); + ratio = mix(ratio, vec3(white), vec3(pow(peak, crosstalk))); + ratio = pow(abs(ratio), vec3(crossSaturation)); + + // Then apply ratio to peak. + color = peak * ratio; + return color; +} + +//-------------------------------------------------------------------------------------- +// The tone mapper used in HDRToneMappingCS11 +//-------------------------------------------------------------------------------------- +vec3 DX11DSK(vec3 color) +{ + const float middleGray = 0.72; + const float lumWhite = 1.5; + + // Tone mapping. + color *= middleGray; + color *= (1.0 + color / lumWhite); + color /= (1.0 + color); + + return color; +} + +//-------------------------------------------------------------------------------------- +// Reinhard +//-------------------------------------------------------------------------------------- +vec3 Reinhard(vec3 color) +{ + return color / (1.0 + color); +} + +//-------------------------------------------------------------------------------------- +// Hable's filmic +//-------------------------------------------------------------------------------------- +vec3 Uncharted2TonemapOp(vec3 x) +{ + const float A = 0.15; + const float B = 0.50; + const float C = 0.10; + const float D = 0.20; + const float E = 0.02; + const float F = 0.30; + + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} + +vec3 Uncharted2Tonemap(vec3 color) +{ + const float W = 11.2; + return Uncharted2TonemapOp(2.0 * color) / Uncharted2TonemapOp(vec3(W)); +} + +//-------------------------------------------------------------------------------------- +// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +//-------------------------------------------------------------------------------------- +vec3 ACESFilm(vec3 x) +{ + const float a = 2.51; + const float b = 0.03; + const float c = 2.43; + const float d = 0.59; + const float e = 0.14; + return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0); +} + +//-------------------------------------------------------------------------------------- +// Default switch +//-------------------------------------------------------------------------------------- +vec3 Tonemap(vec3 color, float exposure, int tonemapper) +{ + color *= exposure; + + switch (tonemapper) + { + case 0: return TimothyTonemapper(color); + case 1: return DX11DSK(color); + case 2: return Reinhard(color); + case 3: return Uncharted2Tonemap(color); + case 4: return ACESFilm(color); + case 5: return color; + default: return vec3(1.0); + } +} From 4cdf68c97a6a07fde63724f4d6b4722e9fc1ac50 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Fri, 14 Aug 2026 06:25:00 +0800 Subject: [PATCH 5/6] feat(rendering): batch GPU occlusion queries with dynamic uniforms --- .../lib/v2/rendering/ALRComputePipelines.java | 2 +- .../blaze3d/query/gl/GlSamplesQuery.java | 4 +- ...ffer.java => GpuReusableResourcePool.java} | 4 +- .../buffers/object/BufferObject.java | 9 +- .../occlusion/query/FrameState.java | 117 ++++----- .../query/GpuQueryOcclusionCuller.java | 48 +++- ...ingBuffer.java => GpuSampleQueryPool.java} | 8 +- .../occlusion/query/QueryBufferPack.java | 160 ------------ .../query/QueryBufferPackRingBuffer.java | 14 - .../occlusion/query/QueryInstance.java | 239 ++++++++++++++++++ .../occlusion/query/QueryInstancePool.java | 16 ++ 11 files changed, 355 insertions(+), 266 deletions(-) rename module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/{GpuReusableResourceRingBuffer.java => GpuReusableResourcePool.java} (80%) rename module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/{GpuSampleQueryRingBuffer.java => GpuSampleQueryPool.java} (69%) delete mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java delete mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstance.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstancePool.java diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java index afdd465b..6141e1fd 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java @@ -15,7 +15,7 @@ public class ALRComputePipelines { .withDefines( ShaderDefines.builder() .define("FFX_SPD_OPTION_DOWNSAMPLE_FILTER", "2") // use max for HZB - .define("FFX_SPD_OPTION_WAVE_INTEROP_LDS", ALROptions.SPD_OPTION_WAVE_INTEROP_LDS ? 0 : 1) // weird reverted + .define("FFX_SPD_OPTION_WAVE_INTEROP_LDS", ALROptions.SPD_OPTION_WAVE_INTEROP_LDS ? 0 : 1) // weird inverted .build() ) .withTexture("r_input_downsample_src") // a sampler2DArray in shader, changes needed? diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java index f9a6f5c9..317248b8 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/query/gl/GlSamplesQuery.java @@ -26,7 +26,7 @@ public void begin() { @Override public void end() { - GL46.glEndQuery(id); + GL46.glEndQuery(GL46.GL_ANY_SAMPLES_PASSED); } @Override @@ -39,7 +39,7 @@ public long getValue() { try (MemoryStack stack = MemoryStack.stackPush()) { long buffer = stack.nmalloc(4); GL46.glGetQueryObjectuiv(id, GL46.GL_QUERY_RESULT, buffer); - this.result = MemoryAccess.getLong(buffer); + this.result = MemoryAccess.getInt(buffer); } } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourcePool.java similarity index 80% rename from module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java rename to module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourcePool.java index 101f23e8..003e0f1e 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourceRingBuffer.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/GpuReusableResourcePool.java @@ -1,8 +1,8 @@ package dev.anvilcraft.lib.v2.rendering.foundation; -public abstract class GpuReusableResourceRingBuffer extends LoopResetPool { +public abstract class GpuReusableResourcePool extends LoopResetPool { - public GpuReusableResourceRingBuffer(int size, C context) { + public GpuReusableResourcePool(int size, C context) { super(size, context); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java index a0c1ff2a..1c93ec4f 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java @@ -33,14 +33,21 @@ public void upload(CommandEncoder commandEncoder, GpuBufferSlice dest) { @Override @SuppressWarnings("unchecked") public void write(@NonNull ByteBuffer buffer) { + if (this.usage == ShaderBufferObjectUsage.SSBO) { + throw new IllegalStateException(); + } getDefinition().write(buffer, (T) this, this.layout); } + @Deprecated(forRemoval = true) public DynamicUniformStorage createDynamicStorage(String label) { + if (this.usage == ShaderBufferObjectUsage.SSBO) { + throw new IllegalStateException(); + } return new DynamicUniformStorage<>( label, getDefinition().size(this.layout), - GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_COPY_DST + 16 ); } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java index d8571f25..86dca0cb 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/FrameState.java @@ -1,6 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; @@ -15,22 +16,21 @@ import it.unimi.dsi.fastutil.objects.Reference2LongLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2LongMap; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.DynamicUniformStorage; import net.minecraft.client.renderer.state.level.CameraRenderState; -import net.minecraft.world.phys.AABB; -import org.joml.Matrix4f; -import org.joml.Vector3f; +import org.jetbrains.annotations.ApiStatus; -import java.util.HashSet; +import java.util.ArrayList; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.OptionalDouble; import java.util.OptionalInt; -import java.util.Set; -class FrameState implements AutoCloseable { +@ApiStatus.Internal +public class FrameState implements AutoCloseable { private final Map keySamplesMap = new IdentityHashMap<>(); private final Reference2LongMap results = new Reference2LongLinkedOpenHashMap<>(); - private final Set keys = new HashSet<>(); private final GpuQueryOcclusionCuller owner; public FrameState(GpuQueryOcclusionCuller owner) { @@ -45,7 +45,7 @@ public void addKey(OcclusionKey key) { } public boolean shouldDraw(OcclusionKey key) { - return results.getOrDefault(key, 0) > 0; + return results.getOrDefault(key, 1) > 0; } public void fetchResults() { @@ -56,11 +56,6 @@ public void fetchResults() { @SuppressWarnings("DataFlowIssue") public void runQueries(CameraRenderState camera) { - QueryBufferPack bufferPack = this.owner.acquireBuffer(); - FullTransformsUbo transformsUbo = bufferPack.transformsUbo(); - - transformsUbo.getProjMat().set(camera.projectionMatrix); - CommandEncoder commandEncoder = this.owner.getCommandEncoder(); RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); @@ -70,77 +65,57 @@ public void runQueries(CameraRenderState camera) { GpuDevice device = RenderSystem.getDevice(); - int instanceId = 0; - ALRGpuDeviceExtension deviceExtension = (ALRGpuDeviceExtension) device; deviceExtension.alrPushDebugGroup(() -> "Gpu Occlusion Query Draw"); + List queries = new ArrayList<>(); + for (Map.Entry entry : keySamplesMap.entrySet()) { - int finalInstanceId = instanceId; - - OcclusionKey key = entry.getKey(); - GpuQueryObject query = entry.getValue(); - - Matrix4f modelViewMat = transformsUbo.getModelViewMat(); - modelViewMat.set(camera.viewRotationMatrix); - modelViewMat.translate( - (float) -camera.pos.x, - (float) -camera.pos.y, - (float) -camera.pos.z - ); - - AABB boundingBox = key.getBoundingBox(); - - Vector3f min = new Vector3f( - (float) boundingBox.minX, - (float) boundingBox.minY, - (float) boundingBox.minZ - ); - - Vector3f max = new Vector3f( - (float) boundingBox.maxX, - (float) boundingBox.maxY, - (float) boundingBox.maxZ - ); - - // ChatGPT can make mistakes. Check important info. - Matrix4f transformation = new Matrix4f() - .translate(min) - .scale( - max.x - min.x, - max.y - min.y, - max.z - min.z - ); - - modelViewMat.mul(transformation); - - transformsUbo.upload(commandEncoder, bufferPack.transformsBuffer().slice()); - - try (RenderPass renderPass = commandEncoder.createRenderPass( - () -> "Gpu Occlusion Query Draw #" + finalInstanceId, - target.getColorTextureView(), - OptionalInt.empty(), - target.getDepthTextureView(), - OptionalDouble.empty() - )) { - renderPass.setPipeline(ALRPipelines.OCCLUSION_QUERY); - - renderPass.setVertexBuffer(0, bufferPack.vertexBuffer()); - renderPass.setIndexBuffer(buffer, type); + QueryInstance query = this.owner.acquireInstance(); + query.prepareTransform(entry.getKey(), entry.getValue(), camera); + queries.add(query); + } - renderPass.setUniform("Transforms", bufferPack.transformsBuffer()); + FullTransformsUbo[] transforms = new FullTransformsUbo[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + transforms[i] = queries.get(i).transformsUbo(); + } + DynamicUniformStorage dynamicStorage = this.owner.getTransformsDynamicStorage(); - query.begin(); + GpuBufferSlice[] gpuBufferSlices = dynamicStorage.writeUniforms(transforms); + for (int i = 0; i < queries.size(); i++) { + queries.get(i).uniform(gpuBufferSlices[i]); + } + + try (RenderPass renderPass = commandEncoder.createRenderPass( + () -> "Gpu Occlusion Query Draw Batch", + target.getColorTextureView(), + OptionalInt.empty(), + target.getDepthTextureView(), + OptionalDouble.empty() + )) { + renderPass.setPipeline(ALRPipelines.OCCLUSION_QUERY); + + for (QueryInstance query : queries) { + renderPass.setUniform("Transforms", query.uniform()); + renderPass.setVertexBuffer(0, query.vertexBuffer()); + renderPass.setIndexBuffer(buffer, type); + + query.queryObject().begin(); renderPass.drawIndexed(0, 0, 6 * 6, 1); - query.end(); + query.queryObject().end(); } } - deviceExtension.alrPopDebugGroup(); + for (QueryInstance query : queries) { + this.owner.releaseInstance(query); + } + + dynamicStorage.endFrame(); - this.owner.releaseBuffer(bufferPack); + deviceExtension.alrPopDebugGroup(); } @Override diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java index c15f2634..7751c425 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java @@ -4,34 +4,58 @@ import com.mojang.blaze3d.systems.GpuDevice; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo.FullTransformsUbo; import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; import lombok.Getter; -import net.minecraft.client.Camera; +import net.minecraft.client.renderer.DynamicUniformStorage; import net.minecraft.client.renderer.state.level.CameraRenderState; +import org.jetbrains.annotations.ApiStatus; +/// ### How does this work +/// In a single frame query happens in order described below. +/// - Frame begin +/// - Read back query result in previous frame +/// - Minecraft collects features to draw in current frame +/// - Culler collects OcclusionKey for features requested to cull +/// - Minecraft draws solid terrain +/// - Culler draws bounding box of each OcclusionKey and submits query about whether any samples passed in each draw event +/// - Minecraft draws features +/// - Ask culler whether a feature should be drawn, answers are based on the previous frame's query results (one-frame latency) +/// - ... +/// - Frame ends +@ApiStatus.Internal public class GpuQueryOcclusionCuller implements OcclusionCuller { - @Getter private final CommandEncoder commandEncoder; - private final GpuSampleQueryRingBuffer sampleQueryPool; - private final QueryBufferPackRingBuffer bufferPackPool; + private final GpuSampleQueryPool sampleQueryPool; + private final QueryInstancePool queryInstancePool; private final ALRGpuDeviceExtension extension; + @Getter + private final DynamicUniformStorage transformsDynamicStorage; private FrameState previousFrameState = null; private FrameState currentFrameState = null; public GpuQueryOcclusionCuller(ALRGpuDeviceExtension extension) { this.extension = extension; - this.sampleQueryPool = new GpuSampleQueryRingBuffer(extension); + this.sampleQueryPool = new GpuSampleQueryPool(extension); GpuDevice gpuDevice = (GpuDevice) extension; + this.commandEncoder = gpuDevice.createCommandEncoder(); - this.bufferPackPool = new QueryBufferPackRingBuffer( - new QueryBufferPack.CreationContext( + + this.queryInstancePool = new QueryInstancePool( + new QueryInstance.CreationContext( commandEncoder, gpuDevice ) ); + + this.transformsDynamicStorage = new DynamicUniformStorage<>( + "Gpu Query Occlusion Transforms Dynamic Uniform", + FullTransformsUbo.SIZE, + 512 + ); } @Override @@ -61,7 +85,7 @@ public void processFeatures(CameraRenderState camera) { @Override public boolean shouldDraw(OcclusionKey key, Object feature) { if (previousFrameState == null) { - return false; + return true; } return this.previousFrameState.shouldDraw(key); } @@ -74,11 +98,11 @@ public void releaseQuery(GpuQueryObject query) { this.sampleQueryPool.release(query); } - public QueryBufferPack acquireBuffer() { - return this.bufferPackPool.acquire(); + public QueryInstance acquireInstance() { + return this.queryInstancePool.acquire(); } - public void releaseBuffer(QueryBufferPack bufferPack) { - this.bufferPackPool.release(bufferPack); + public void releaseInstance(QueryInstance queryInstance) { + this.queryInstancePool.release(queryInstance); } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryPool.java similarity index 69% rename from module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java rename to module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryPool.java index cf18ddb0..42ff4c08 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryRingBuffer.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuSampleQueryPool.java @@ -2,11 +2,13 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; -import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourceRingBuffer; +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourcePool; +import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NonNull; -public class GpuSampleQueryRingBuffer extends GpuReusableResourceRingBuffer { - public GpuSampleQueryRingBuffer(ALRGpuDeviceExtension context) { +@ApiStatus.Internal +public class GpuSampleQueryPool extends GpuReusableResourcePool { + public GpuSampleQueryPool(ALRGpuDeviceExtension context) { super(16, context); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java deleted file mode 100644 index 02655a06..00000000 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPack.java +++ /dev/null @@ -1,160 +0,0 @@ -package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; - -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.systems.CommandEncoder; -import com.mojang.blaze3d.systems.GpuDevice; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexFormat; -import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResource; -import dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo.FullTransformsUbo; - -public final class QueryBufferPack implements GpuReusableResource { - - /// each bounding box 3 * 4 * 4 * 6 byte = 288 byte, - /// total 4096 bounding boxes = 1.1 MiB - /// - /// no that's shit, - /// use single unit box and model view matrix instead - /// ((3 float * 4) for each quad * 6) for cube * 1 - public static final int DEFAULT_VERTEX_BUFFER_SIZE = 3 * 4 * 4 * 6; - - private final GpuBuffer vertexBuffer; - private final GpuBuffer transformsBuffer; - private final FullTransformsUbo transformsUbo; - - private boolean closed = false; - private boolean acquired = false; - - public QueryBufferPack( - GpuBuffer vertexBuffer, - GpuBuffer transformsBuffer, - FullTransformsUbo transformsUbo - ) { - this.vertexBuffer = vertexBuffer; - this.transformsBuffer = transformsBuffer; - this.transformsUbo = transformsUbo; - } - - - public static QueryBufferPack newInstance(CreationContext context, int index) { - QueryBufferPack bufferPack = new QueryBufferPack( - context.device.createBuffer( - () -> "Gpu Occlusion Query Vertex Buffer #" + index, - GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_COPY_DST, - DEFAULT_VERTEX_BUFFER_SIZE - ), - context.device.createBuffer( - () -> "Gpu Occlusion Query Uniform Buffer #" + index, - GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_COPY_DST, - FullTransformsUbo.SIZE - ), - new FullTransformsUbo() - ); - - prepareMesh(context, bufferPack); - - return bufferPack; - } - - private static void prepareMesh(CreationContext context, QueryBufferPack bufferPack) { - float x0 = 0; - float y0 = 0; - float z0 = 0; - float x1 = 1; - float y1 = 1; - float z1 = 1; - - BufferBuilder bufferBuilder = Tesselator.getInstance() - .begin( - VertexFormat.Mode.QUADS, - DefaultVertexFormat.POSITION - ); - - // z+ - bufferBuilder.addVertex(x0, y0, z1); - bufferBuilder.addVertex(x1, y0, z1); - bufferBuilder.addVertex(x1, y1, z1); - bufferBuilder.addVertex(x0, y1, z1); - - // z- - bufferBuilder.addVertex(x0, y0, z0); - bufferBuilder.addVertex(x0, y1, z0); - bufferBuilder.addVertex(x1, y1, z0); - bufferBuilder.addVertex(x1, y0, z0); - - // x+ - bufferBuilder.addVertex(x1, y0, z0); - bufferBuilder.addVertex(x1, y0, z1); - bufferBuilder.addVertex(x1, y1, z1); - bufferBuilder.addVertex(x1, y1, z0); - - // x- - bufferBuilder.addVertex(x0, y0, z0); - bufferBuilder.addVertex(x0, y1, z0); - bufferBuilder.addVertex(x0, y1, z1); - bufferBuilder.addVertex(x0, y0, z1); - - // y+ - bufferBuilder.addVertex(x0, y1, z0); - bufferBuilder.addVertex(x0, y1, z1); - bufferBuilder.addVertex(x1, y1, z1); - bufferBuilder.addVertex(x1, y1, z0); - - // y- - bufferBuilder.addVertex(x0, y0, z0); - bufferBuilder.addVertex(x1, y0, z0); - bufferBuilder.addVertex(x1, y0, z1); - bufferBuilder.addVertex(x0, y0, z1); - - MeshData orThrow = bufferBuilder.buildOrThrow(); - - context.commandEncoder.writeToBuffer(bufferPack.vertexBuffer.slice(), orThrow.vertexBuffer()); - - orThrow.close(); - } - - @Override - public void acquire() { - this.acquired = true; - } - - @Override - public void release() { - this.acquired = false; - } - - @Override - public boolean isAcquired() { - return this.acquired; - } - - @Override - public void close() { - if (!closed) { - this.vertexBuffer.close(); - this.transformsBuffer.close(); - this.closed = true; - } - } - - public GpuBuffer vertexBuffer() { - return vertexBuffer; - } - - public GpuBuffer transformsBuffer() { - return transformsBuffer; - } - - public FullTransformsUbo transformsUbo() { - return transformsUbo; - } - - public record CreationContext( - CommandEncoder commandEncoder, - GpuDevice device - ) { - } -} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java deleted file mode 100644 index 3b179583..00000000 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryBufferPackRingBuffer.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; - -import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourceRingBuffer; - -public class QueryBufferPackRingBuffer extends GpuReusableResourceRingBuffer { - public QueryBufferPackRingBuffer(QueryBufferPack.CreationContext context) { - super(2, context); - } - - @Override - protected QueryBufferPack createInstance(QueryBufferPack.CreationContext context, int i) { - return QueryBufferPack.newInstance(context, i); - } -} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstance.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstance.java new file mode 100644 index 00000000..1f20db11 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstance.java @@ -0,0 +1,239 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResource; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.ubo.FullTransformsUbo; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.ApiStatus; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +@ApiStatus.Internal +public final class QueryInstance implements GpuReusableResource { + + /// use single unit box and model view matrix instead + /// ((3 float * 4) for each quad * 6) for cube * 1 + public static final int DEFAULT_VERTEX_BUFFER_SIZE = 3 * 4 * 4 * 6; + + /// Shared between QueryInstance instances + private final GpuBuffer vertexBuffer; + + private final FullTransformsUbo transformsUbo; + + @Nullable + private OcclusionKey key; + + @Nullable + private GpuQueryObject queryObject; + @Nullable + private GpuBufferSlice uniform; + + private boolean closed = false; + private boolean acquired = false; + + public QueryInstance( + GpuBuffer vertexBuffer, + FullTransformsUbo transformsUbo + ) { + this.vertexBuffer = vertexBuffer; + this.transformsUbo = transformsUbo; + } + + void prepareTransform(OcclusionKey key, GpuQueryObject queryObject, CameraRenderState camera) { + this.key = key; + this.queryObject = queryObject; + + this.transformsUbo.getProjMat().set(camera.projectionMatrix); + + Matrix4f modelViewMat = this.transformsUbo.getModelViewMat(); + modelViewMat.set(camera.viewRotationMatrix); + + Vec3 cameraPos = camera.pos; + modelViewMat.translate( + (float) -cameraPos.x, + (float) -cameraPos.y, + (float) -cameraPos.z + ); + + AABB boundingBox = key.getBoundingBox().inflate(0.1); + + Vector3f min = new Vector3f( + (float) boundingBox.minX, + (float) boundingBox.minY, + (float) boundingBox.minZ + ); + + Vector3f max = new Vector3f( + (float) boundingBox.maxX, + (float) boundingBox.maxY, + (float) boundingBox.maxZ + ); + + // ChatGPT can make mistakes. Check important info. + Matrix4f transformation = new Matrix4f() + .translate(min) + .scale( + max.x - min.x, + max.y - min.y, + max.z - min.z + ); + + modelViewMat.mul(transformation); + } + + @Override + public void acquire() { + this.acquired = true; + } + + @Override + public void release() { + this.acquired = false; + this.key = null; + this.queryObject = null; + this.uniform = null; + } + + @Override + public boolean isAcquired() { + return this.acquired; + } + + @Override + public void close() { + if (!closed) { + // nothing to close if we handle all lifecycle logic correct + this.closed = true; + } + } + + public GpuBuffer vertexBuffer() { + return vertexBuffer; + } + + public FullTransformsUbo transformsUbo() { + return transformsUbo; + } + + @Nullable + public OcclusionKey key() { + return key; + } + + @Nullable + public GpuQueryObject queryObject() { + return queryObject; + } + + @Nullable + public GpuBufferSlice uniform() { + return uniform; + } + + public void uniform(GpuBufferSlice uniform) { + this.uniform = uniform; + } + + public static QueryInstance newInstance(CreationContext context, int index) { + return new QueryInstance( + context.getVertexBuffer(), + new FullTransformsUbo() + ); + } + + public record CreationContext( + CommandEncoder commandEncoder, + GpuDevice device + ) { + /// should keep alive when game running + private static GpuBuffer vertexBuffer; + + @NonNull + public GpuBuffer getVertexBuffer() { + if (vertexBuffer == null) { + prepareMesh(this); + } + return vertexBuffer; + } + + private static void prepareMesh(CreationContext context) { + float x0 = 0; + float y0 = 0; + float z0 = 0; + float x1 = 1; + float y1 = 1; + float z1 = 1; + + BufferBuilder bufferBuilder = Tesselator.getInstance() + .begin( + VertexFormat.Mode.QUADS, + DefaultVertexFormat.POSITION + ); + + // z+ + bufferBuilder.addVertex(x0, y0, z1); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x0, y1, z1); + + // z- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x1, y1, z0); + bufferBuilder.addVertex(x1, y0, z0); + + // x+ + bufferBuilder.addVertex(x1, y0, z0); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x1, y1, z0); + + // x- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x0, y1, z1); + bufferBuilder.addVertex(x0, y0, z1); + + // y+ + bufferBuilder.addVertex(x0, y1, z0); + bufferBuilder.addVertex(x0, y1, z1); + bufferBuilder.addVertex(x1, y1, z1); + bufferBuilder.addVertex(x1, y1, z0); + + // y- + bufferBuilder.addVertex(x0, y0, z0); + bufferBuilder.addVertex(x1, y0, z0); + bufferBuilder.addVertex(x1, y0, z1); + bufferBuilder.addVertex(x0, y0, z1); + + MeshData orThrow = bufferBuilder.buildOrThrow(); + + if (vertexBuffer == null) { + vertexBuffer = context.device.createBuffer( + () -> "Gpu Occlusion Query Vertex Buffer", + GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_COPY_DST, + DEFAULT_VERTEX_BUFFER_SIZE + ); + } + + context.commandEncoder.writeToBuffer(vertexBuffer.slice(), orThrow.vertexBuffer()); + + orThrow.close(); + } + + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstancePool.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstancePool.java new file mode 100644 index 00000000..60259d2a --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/QueryInstancePool.java @@ -0,0 +1,16 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query; + +import dev.anvilcraft.lib.v2.rendering.foundation.GpuReusableResourcePool; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public class QueryInstancePool extends GpuReusableResourcePool { + public QueryInstancePool(QueryInstance.CreationContext context) { + super(2, context); + } + + @Override + protected QueryInstance createInstance(QueryInstance.CreationContext context, int i) { + return QueryInstance.newInstance(context, i); + } +} From 6a630c8f51f0a7de3c6794b4e0f8523e9149b9cd Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Sat, 15 Aug 2026 07:29:26 +0800 Subject: [PATCH 6/6] feat(rendering): add Hi-Z texture culling foundation --- .../lib/v2/rendering/ALRComputePipelines.java | 15 +- .../lib/v2/rendering/ALROptimizations.java | 4 + .../blaze3d/ALRGpuDeviceBackendExtension.java | 12 + .../blaze3d/ALRGpuDeviceExtension.java | 13 ++ .../blaze3d/ExtendedTextureFormat.java | 26 +++ .../compute/pipeline/ALRComputePass.java | 6 +- .../compute/pipeline/ALRComputePipeline.java | 6 + .../bindings/ComputeBindingLayout.java | 6 + .../pipeline/bindings/ImageArrayBinding.java | 38 ++++ .../blaze3d/texture/ExtendedGpuTexture.java | 7 + .../blaze3d/texture/gl/GlExtendedTexture.java | 39 ++++ .../gl/GlExtendedTextureConstants.java | 34 +++ .../buffers/object/BufferObject.java | 1 - .../v2/rendering/mixins/MinecraftMixin.java | 2 + .../mixins/blaze3d/GpuDeviceMixin.java | 18 ++ .../mixins/blaze3d/gl/GlDeviceMixin.java | 87 ++++++++ .../occlusion/OcclusionCuller.java | 1 + .../occlusion/OcclusionMethod.java | 5 +- .../occlusion/hiz/ConvertDepthParamsUbo.java | 4 +- .../hiz/HierarchicalZOcclusionCuller.java | 190 ++++++++++++++++ .../hiz/HierarchicalZOcculusionCuller.java | 28 --- .../optimization/occlusion/hiz/MipLayer.java | 25 +++ .../occlusion/hiz/SPDConstantBuffer.java | 5 +- .../query/GpuQueryOcclusionCuller.java | 5 + .../lib/v2/rendering/util/MemoryAccess.java | 46 +++- .../shaders/compute/depth_convert.csh | 12 +- .../compute/ffx_spd_downsample_pass.csh | 205 +++++++++--------- 27 files changed, 690 insertions(+), 150 deletions(-) create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ExtendedTextureFormat.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageArrayBinding.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/ExtendedGpuTexture.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTexture.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTextureConstants.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcclusionCuller.java delete mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java create mode 100644 module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/MipLayer.java diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java index 6141e1fd..a37c9458 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALRComputePipelines.java @@ -18,14 +18,23 @@ public class ALRComputePipelines { .define("FFX_SPD_OPTION_WAVE_INTEROP_LDS", ALROptions.SPD_OPTION_WAVE_INTEROP_LDS ? 0 : 1) // weird inverted .build() ) - .withTexture("r_input_downsample_src") // a sampler2DArray in shader, changes needed? - .withReadWriteImage("rw_input_downsample_src_mid_mip") // a image2DArray in shader, changes needed? - .withReadWriteImage("rw_input_downsample_src_mips") // an array of image2DArray in shader, changes needed? length=13 + .withUniformBlock("cbFSR1") + .withTexture("r_input_downsample_src") + .withReadWriteImage("rw_input_downsample_src_mid_mip") + .withImageArray("rw_input_downsample_src_mips", true, true, 13) .build(); + public static final ALRComputePipeline DEPTH_CONVERT = ALRComputePipeline.builder() + .withName(AnvilLibRendering.location("depth_convert")) + .withShader(AnvilLibRendering.location("compute/depth_convert.csh")) + .withUniformBlock("ConvertParam") + .withTexture("Input") + .withWriteOnlyImage("Output") + .build(); @SubscribeEvent public static void on(RegisterComputePipelinesEvent event) { event.registerPipeline(FFX_SPD_DOWNSAMPLE_PASS); + event.registerPipeline(DEPTH_CONVERT); } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java index 351e3e59..77561dd1 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/ALROptimizations.java @@ -14,4 +14,8 @@ public static OcclusionCuller getOcclusionCuller() { } return occlusionCuller; } + + public static void create() { + occlusionCuller = OcclusionCuller.createInstance(RenderSystem.getDevice()); + } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java index a268bb61..3b336575 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceBackendExtension.java @@ -1,10 +1,12 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; +import com.mojang.blaze3d.textures.GpuTexture; import org.jetbrains.annotations.ApiStatus; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import org.jspecify.annotations.Nullable; import java.util.function.Supplier; @@ -21,4 +23,14 @@ public interface ALRGpuDeviceBackendExtension { GpuQueryObject alrCreateSamplesQuery(); ALRHICapabilities alrhiCreateCapabilities(); + + GpuTexture alrCreateExtendedTexture( + @Nullable String label, + @GpuTexture.Usage int usage, + ExtendedTextureFormat format, + int width, + int height, + int depthOrLayers, + int mipLevels + ); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java index 84e31837..26709ec2 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java @@ -1,8 +1,11 @@ package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; +import com.mojang.blaze3d.textures.GpuTexture; +import com.mojang.blaze3d.textures.TextureFormat; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import org.jspecify.annotations.Nullable; import java.util.function.Supplier; @@ -18,4 +21,14 @@ public interface ALRGpuDeviceExtension { void alrPopDebugGroup(); ALRHICapabilities alrhiCreateCapabilities(); + + GpuTexture alrCreateExtendedTexture( + @Nullable String label, + @GpuTexture.Usage int usage, + ExtendedTextureFormat format, + int width, + int height, + int depthOrLayers, + int mipLevels + ); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ExtendedTextureFormat.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ExtendedTextureFormat.java new file mode 100644 index 00000000..0d576b0e --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ExtendedTextureFormat.java @@ -0,0 +1,26 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d; + +import net.neoforged.neoforge.internal.NonExhaustiveEnum; + +@SuppressWarnings("UnstableApiUsage") +@NonExhaustiveEnum(reason = "Additional texture formats may be added") +public enum ExtendedTextureFormat { + /// Single-channel 32-bit float, GL_R32F. Depth/Hi-Z pipelines. + R32F(4), + /// Four-channel 16-bit float, GL_RGBA16F. Common HDR color target. + RGBA16F(8), + /// Four-channel 32-bit float, GL_RGBA32F + RGBA32F(16), + /// 10-bit RGB with 2-bit alpha, GL_RGB10_A2 + RGB10_A2(4); + + private final int pixelSize; + + ExtendedTextureFormat(int pixelSize) { + this.pixelSize = pixelSize; + } + + public int pixelSize() { + return this.pixelSize; + } +} \ No newline at end of file diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java index 4b78ec51..a3def33e 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java @@ -59,12 +59,12 @@ public void setPipeline(ALRComputePipeline pipeline) { public void bindAll(List elements) { int bindingPoint = 0; for (ComputeBindingLayout binding : pipeline.bindings()) { - this.bind(bindingPoint++, binding, elements.get(bindingPoint - 1)); + bindingPoint += this.bind(bindingPoint, binding, elements.get(bindingPoint - 1)); } } - public void bind(int bindingPoint, ComputeBindingLayout layout, T resource) { - layout.apply(bindingPoint, resource, this); + public int bind(int bindingPointStart, ComputeBindingLayout layout, T resource) { + return layout.applyOrdered(bindingPointStart, resource, this); } public void bindTexture(int bindingPoint, TextureBinding.SamplerAndTexture resource) { diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java index 7adab954..8944a2ca 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java @@ -2,6 +2,7 @@ import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageArrayBinding; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding; @@ -86,6 +87,11 @@ public Builder withReadWriteImage(String name) { return this.withImage(name, true, true); } + /// an array of image2D, not image2DArray + public Builder withImageArray(String name, boolean read, boolean write, int size) { + return this.withBinding(new ImageArrayBinding(name, read, write, size)); + } + public Builder withUniformBlock(String name) { return this.withBinding(new UniformBlockBinding(name)); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java index b9bbb054..06f90630 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java @@ -9,4 +9,10 @@ public interface ComputeBindingLayout { String name(); void apply(int bindingPoint, T resource, ALRComputePass computePass); + + /// @return binding point incremental + default int applyOrdered(int bindingPointStart, T resource, ALRComputePass computePass) { + this.apply(bindingPointStart, resource, computePass); + return 1; + } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageArrayBinding.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageArrayBinding.java new file mode 100644 index 00000000..2fac784e --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageArrayBinding.java @@ -0,0 +1,38 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings; + +import com.google.common.base.Preconditions; +import com.mojang.blaze3d.textures.GpuTexture; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType; + +import java.util.List; + +public record ImageArrayBinding ( + String name, + boolean read, + boolean write, + int size +) implements ComputeBindingLayout> { + + public ImageArrayBinding { + if (!read && !write) { + throw new IllegalArgumentException("ImageResource does not allow both read and write are false"); + } + } + + @Override + public ShaderResourceType type() { + return ShaderResourceType.IMAGE; + } + + /// Just assume iterating over the list passed in is ordered. + /// + /// `resource.size <= this.size` is allowed, but `resource` must not have elements more than `this.size` + @Override + public void apply(int bindingPoint, List resource, ALRComputePass computePass) { + Preconditions.checkElementIndex(size, resource.size(), "resource must not have elements more than this.size"); + for (GpuTexture texture : resource) { + computePass.bindImage(bindingPoint++, texture, read, write); + } + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/ExtendedGpuTexture.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/ExtendedGpuTexture.java new file mode 100644 index 00000000..52903a54 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/ExtendedGpuTexture.java @@ -0,0 +1,7 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture; + +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; + +public interface ExtendedGpuTexture { + ExtendedTextureFormat getActualFormat(); +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTexture.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTexture.java new file mode 100644 index 00000000..763132b3 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTexture.java @@ -0,0 +1,39 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture.gl; + +import com.mojang.blaze3d.opengl.GlTexture; +import com.mojang.blaze3d.textures.TextureFormat; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture.ExtendedGpuTexture; +import lombok.Getter; + +/// A simple texture implementation with extended formats +public class GlExtendedTexture extends GlTexture implements ExtendedGpuTexture { + + @Getter + private final ExtendedTextureFormat actualFormat; + + public GlExtendedTexture( + @Usage int usage, + String label, + ExtendedTextureFormat format, + int width, + int height, + int depthOrLayers, + int mipLevels, + int id + ) { + // use RGBA8 here hopefully make most of blaze3d code work as they don't care pixel size of actual format + // as we are not using this as a framebuffer attachment + super( + usage, + label, + TextureFormat.RGBA8, + width, + height, + depthOrLayers, + mipLevels, + id + ); + this.actualFormat = format; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTextureConstants.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTextureConstants.java new file mode 100644 index 00000000..6fbd3bec --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/GlExtendedTextureConstants.java @@ -0,0 +1,34 @@ +package dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture.gl; + +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; +import org.lwjgl.opengl.GL46; + +public class GlExtendedTextureConstants { + public static int toGlConst(ExtendedTextureFormat textureFormat){ + return switch (textureFormat) { + case R32F -> GL46.GL_R32F; + case RGBA16F -> GL46.GL_RGBA16F; + case RGBA32F -> GL46.GL_RGBA32F; + case RGB10_A2 -> GL46.GL_RGB10_A2; + }; + } + + public static int toGlInternalId(ExtendedTextureFormat format) { + return toGlConst(format); + } + + public static int toGlExternalId(ExtendedTextureFormat format) { + return switch (format) { + case R32F -> GL46.GL_RED; + case RGBA16F, RGBA32F, RGB10_A2 -> GL46.GL_RGBA; + }; + } + + public static int toGlType(ExtendedTextureFormat format) { + return switch (format) { + case R32F, RGBA32F -> GL46.GL_FLOAT; + case RGBA16F -> GL46.GL_HALF_FLOAT; + case RGB10_A2 -> GL46.GL_UNSIGNED_INT_2_10_10_10_REV; + }; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java index 1c93ec4f..5476ee83 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/foundation/buffers/object/BufferObject.java @@ -1,6 +1,5 @@ package dev.anvilcraft.lib.v2.rendering.foundation.buffers.object; -import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.systems.CommandEncoder; import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java index b03e2f0a..b7fd77bd 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/MinecraftMixin.java @@ -1,6 +1,7 @@ package dev.anvilcraft.lib.v2.rendering.mixins; import com.mojang.blaze3d.platform.Window; +import dev.anvilcraft.lib.v2.rendering.ALROptimizations; import dev.anvilcraft.lib.v2.rendering.ALRPostEffects; import dev.anvilcraft.lib.v2.rendering.cachedber.pipeline.CachedBlockEntityRenderingPipeline; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.ALRComputeCapabilities; @@ -27,6 +28,7 @@ public class MinecraftMixin { ) private void onCreateInstance(GameConfig gameConfig, CallbackInfo ci) { ALRPostEffects.createPostEffects(); + ALROptimizations.create(); CachedBlockEntityRenderingPipeline.create(); ALRComputeCapabilities.init(); } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java index 8956435a..8e7709b3 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/GpuDeviceMixin.java @@ -2,12 +2,15 @@ import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.GpuDeviceBackend; +import com.mojang.blaze3d.textures.GpuTexture; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceBackendExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRHICapabilities; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; +import org.jspecify.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -52,6 +55,21 @@ public ALRHICapabilities alrhiCreateCapabilities() { return alrBackend().alrhiCreateCapabilities(); } + @Override + public GpuTexture alrCreateExtendedTexture( + @Nullable String label, + @GpuTexture.Usage int usage, + ExtendedTextureFormat format, + int width, + int height, + int depthOrLayers, + int mipLevels + ){ + return alrBackend().alrCreateExtendedTexture( + label, usage, format, width, height, depthOrLayers, mipLevels + ); + } + @Unique private ALRGpuDeviceBackendExtension alrBackend() { return ((ALRGpuDeviceBackendExtension) this.backend); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java index 67123a40..bbcd7361 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDeviceMixin.java @@ -1,16 +1,26 @@ package dev.anvilcraft.lib.v2.rendering.mixins.blaze3d.gl; +import com.mojang.blaze3d.GpuOutOfMemoryException; +import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlDebugLabel; +import com.mojang.blaze3d.opengl.GlStateManager; +import com.mojang.blaze3d.opengl.GlTexture; +import com.mojang.blaze3d.textures.GpuTexture; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceBackendExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRHICapabilities; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.ALRDebugLabelExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeShaderManager; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.GpuQueryObject; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.query.gl.GlSamplesQuery; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture.gl.GlExtendedTexture; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.texture.gl.GlExtendedTextureConstants; +import org.jspecify.annotations.Nullable; import org.lwjgl.opengl.ARBComputeShader; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL46; import org.lwjgl.opengl.GLCapabilities; import org.slf4j.Logger; @@ -31,6 +41,9 @@ public abstract class GlDeviceMixin implements ALRGpuDeviceBackendExtension { @Shadow public abstract GlDebugLabel debugLabels(); + @Shadow + @Final + private GlDebugLabel debugLabels; @Unique private ALRHICapabilities alr$capabilities = null; @@ -89,4 +102,78 @@ public ALRHICapabilities alrhiCreateCapabilities() { } return alr$capabilities; } + + @Override + public GpuTexture alrCreateExtendedTexture( + @Nullable String label, + int usage, + ExtendedTextureFormat format, + int width, + int height, + int depthOrLayers, + int mipLevels + ) { + GlStateManager.clearGlErrors(); + int id = GlStateManager._genTexture(); + if (label == null) { + label = String.valueOf(id); + } + + boolean isCubemap = (usage & 16) != 0; + int target; + if (isCubemap) { + GL11.glBindTexture(34067, id); + target = 34067; + } else { + GlStateManager._bindTexture(id); + target = 3553; + } + + GlStateManager._texParameter(target, 33085, mipLevels - 1); + GlStateManager._texParameter(target, 33082, 0); + GlStateManager._texParameter(target, 33083, mipLevels - 1); + + if (isCubemap) { + for (int cubeTarget : GlConst.CUBEMAP_TARGETS) { + for (int i = 0; i < mipLevels; i++) { + GlStateManager._texImage2D( + cubeTarget, + i, + GlExtendedTextureConstants.toGlInternalId(format), + width >> i, + height >> i, + 0, + GlExtendedTextureConstants.toGlExternalId(format), + GlExtendedTextureConstants.toGlType(format), + null + ); + } + } + } else { + for (int i = 0; i < mipLevels; i++) { + GlStateManager._texImage2D( + target, + i, + GlExtendedTextureConstants.toGlInternalId(format), + width >> i, + height >> i, + 0, + GlExtendedTextureConstants.toGlExternalId(format), + GlExtendedTextureConstants.toGlType(format), + null + ); + } + } + + int error = GlStateManager._getError(); + if (error == 1285) { + throw new GpuOutOfMemoryException("Could not allocate texture of " + width + "x" + height + " for " + label); + } else if (error != 0) { + throw new IllegalStateException("OpenGL error " + error); + } else { + GlExtendedTexture texture = new GlExtendedTexture(usage, label, format, width, height, depthOrLayers, mipLevels, id); + this.debugLabels.applyLabel(texture); + return texture; + } + } } diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java index c634a81b..c707ae41 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionCuller.java @@ -5,6 +5,7 @@ import org.jspecify.annotations.Nullable; public interface OcclusionCuller { + void onResize(int newWidth, int newHeight); void beginFrame(); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java index 23bc7495..4b70a9f0 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/OcclusionMethod.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.systems.GpuDevice; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.ALRComputeCapabilities; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz.HierarchicalZOcclusionCuller; import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.query.GpuQueryOcclusionCuller; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -25,8 +26,8 @@ public boolean isSupported() { } @Override - public @Nullable OcclusionCuller createInstance(GpuDevice device) { - return null; + public @NonNull OcclusionCuller createInstance(GpuDevice device) { + return new HierarchicalZOcclusionCuller((ALRGpuDeviceExtension) device); } }; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java index 181f119d..961069e6 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/ConvertDepthParamsUbo.java @@ -22,9 +22,9 @@ public class ConvertDepthParamsUbo extends BufferObject { public static final BufferObjectLayoutDefinition DEFINITION = BufferObjectLayoutDefinition.create( - BufferObjectLayoutEntry.ofInt().forGetter(ConvertDepthParamsUbo::getHeight).build(), BufferObjectLayoutEntry.ofInt().forGetter(ConvertDepthParamsUbo::getWidth).build(), - BufferObjectLayoutEntry.ofFloat().forGetter(ConvertDepthParamsUbo::getPadValue).build() + BufferObjectLayoutEntry.ofInt().forGetter(ConvertDepthParamsUbo::getHeight).build(), + BufferObjectLayoutEntry.ofFloat().forGetter(ConvertDepthParamsUbo::getPadValue).build() ); public static final int SIZE = DEFINITION.size(BufferLayout.STD140); diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcclusionCuller.java new file mode 100644 index 00000000..d1890c01 --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcclusionCuller.java @@ -0,0 +1,190 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.systems.CommandEncoder; +import com.mojang.blaze3d.systems.GpuDevice; +import com.mojang.blaze3d.textures.GpuTexture; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ExtendedTextureFormat; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.GpuBufferConstants; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; +import dev.anvilcraft.lib.v2.rendering.util.MemoryAccess; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.util.Mth; +import org.joml.Vector2f; +import org.lwjgl.system.MemoryStack; + +import java.nio.ByteBuffer; + +public class HierarchicalZOcclusionCuller implements OcclusionCuller { + /// uint * 6 + public static final int SPD_GLOBAL_ATOMIC_COUNTER_SIZE = 4 * 6; + + private final Minecraft minecraft; + private final ALRGpuDeviceExtension gpuDeviceExtension; + private final GpuDevice gpuDevice; + + private final SPDConstantBuffer spdParams = new SPDConstantBuffer(); + private final ConvertDepthParamsUbo convertParams = new ConvertDepthParamsUbo(); + + private final GpuBuffer spdParamsBuffer; + private final GpuBuffer convertParamsBuffer; + private final GpuBuffer spdGlobalAtomicCounterBuffer; + + private int framebufferWidth; + private int framebufferHeight; + private int paddedWidth; + private int paddedHeight; + + private int dispatchDimensionX; + private int dispatchDimensionY; + + /// mip layer count, excluding input layer (mip 0) + private int mipLayerCount = 0; + private GpuTexture[] mipTextures; + private MipLayer[] mipLayers; + + public HierarchicalZOcclusionCuller(ALRGpuDeviceExtension device) { + this.minecraft = Minecraft.getInstance(); + this.gpuDeviceExtension = device; + this.gpuDevice = (GpuDevice) device; + + RenderTarget mainRenderTarget = this.minecraft.getMainRenderTarget(); + + this.spdParamsBuffer = gpuDevice.createBuffer( + () -> "SPD Constant Buffer", + GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_UNIFORM, + SPDConstantBuffer.SIZE + ); + + this.convertParamsBuffer = gpuDevice.createBuffer( + () -> "SPD Depth Convert Params", + GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_UNIFORM, + ConvertDepthParamsUbo.SIZE + ); + + this.spdGlobalAtomicCounterBuffer = gpuDevice.createBuffer( + () -> "SPD Global Atomic Counter", + GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_MAP_READ | GpuBuffer.USAGE_MAP_WRITE | GpuBufferConstants.USAGE_SHADER_STORAGE, + SPD_GLOBAL_ATOMIC_COUNTER_SIZE + ); + + this.onResize(mainRenderTarget.width, mainRenderTarget.height); + } + + @Override + public void onResize(int width, int height) { + this.framebufferWidth = width; + this.framebufferHeight = height; + + this.paddedWidth = Math.ceilDiv(width, 64) * 64; + this.paddedHeight = Math.ceilDiv(height, 64) * 64; + + this.mipLayerCount = Math.min( + Mth.floor( + Mth.log2( + Math.max( + this.paddedWidth, + this.paddedHeight + ) + ) + ), + 12 + ); + + this.dispatchDimensionX = Mth.ceil(paddedWidth / 64f); + this.dispatchDimensionY = Mth.ceil(paddedHeight / 64f); + + this.deleteTextures(); + + int slotCount = mipLayerCount + 1; + this.mipTextures = new GpuTexture[slotCount]; + this.mipLayers = new MipLayer[slotCount]; + + for (int i = 0; i < slotCount; i++) { + int mipW = Math.max(1, this.paddedWidth >> i); + int mipH = Math.max(1, this.paddedHeight >> i); + + MipLayer mipLayer = new MipLayer(); + mipLayer.setWidth(mipW); + mipLayer.setHeight(mipH); + + GpuTexture texture = this.gpuDeviceExtension.alrCreateExtendedTexture( + "HierarchicalZ Mip Chain Image #" + i, + GpuTexture.USAGE_COPY_SRC | GpuTexture.USAGE_COPY_DST | GpuTexture.USAGE_TEXTURE_BINDING, + ExtendedTextureFormat.R32F, + mipW, + mipH, + 1, + 1 + ); + + this.mipTextures[i] = texture; + this.mipLayers[i] = mipLayer; + } + + CommandEncoder commandEncoder = gpuDevice.createCommandEncoder(); + this.ffxSpdSetup(commandEncoder); + this.depthConvertSetup(commandEncoder); + this.clearAtomicCounter(); + } + + /// Setup required constant values for SPD (CPU). + private void ffxSpdSetup(CommandEncoder commandEncoder) { + this.spdParams.setMips(this.mipLayerCount); + this.spdParams.setNumWorkGroups(this.dispatchDimensionX * this.dispatchDimensionY); + this.spdParams.setWorkGroupOffset(new Vector2f(0, 0)); + this.spdParams.setInvInputSize(new Vector2f(1.0f / this.paddedWidth, 1.0f / this.paddedHeight)); + + this.spdParams.upload(commandEncoder, this.spdParamsBuffer.slice()); + } + + private void depthConvertSetup(CommandEncoder commandEncoder) { + this.convertParams.setWidth(this.framebufferWidth); + this.convertParams.setHeight(this.framebufferHeight); + this.convertParams.setPadValue(1); + + this.convertParams.upload(commandEncoder, this.convertParamsBuffer.slice()); + } + + private void clearAtomicCounter() { + CommandEncoder commandEncoder = gpuDevice.createCommandEncoder(); + + try (MemoryStack memoryStack = MemoryStack.stackPush()) { + ByteBuffer buffer = memoryStack.malloc(SPD_GLOBAL_ATOMIC_COUNTER_SIZE); + MemoryAccess.memset(MemoryAccess.memAddress(buffer), SPD_GLOBAL_ATOMIC_COUNTER_SIZE, (byte) 0); + commandEncoder.writeToBuffer(spdGlobalAtomicCounterBuffer.slice(), buffer); + } + } + + @Override + public void beginFrame() { + + } + + @Override + public void submitFeatureKey(OcclusionKey key, Object feature) { + + } + + @Override + public void processFeatures(CameraRenderState camera) { + + } + + @Override + public boolean shouldDraw(OcclusionKey key, Object feature) { + return true; + } + + private void deleteTextures() { + if (mipTextures != null) { + for (GpuTexture mipTexture : mipTextures) { + mipTexture.close(); + } + } + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java deleted file mode 100644 index 9872c395..00000000 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZOcculusionCuller.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; - -import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionCuller; -import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.OcclusionKey; -import net.minecraft.client.Camera; -import net.minecraft.client.renderer.state.level.CameraRenderState; - -public class HierarchicalZOcculusionCuller implements OcclusionCuller { - @Override - public void beginFrame() { - - } - - @Override - public void submitFeatureKey(OcclusionKey key, Object feature) { - - } - - @Override - public void processFeatures(CameraRenderState camera) { - - } - - @Override - public boolean shouldDraw(OcclusionKey key, Object feature) { - return false; - } -} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/MipLayer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/MipLayer.java new file mode 100644 index 00000000..5dc59b2d --- /dev/null +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/MipLayer.java @@ -0,0 +1,25 @@ +package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; + +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.layout.BufferLayout; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObject; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.BufferObjectLayoutDefinition; +import dev.anvilcraft.lib.v2.rendering.foundation.buffers.object.ShaderBufferObjectUsage; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MipLayer extends BufferObject { + + private int width; + private int height; + + protected MipLayer() { + super(BufferLayout.STD430, ShaderBufferObjectUsage.SSBO); + } + + @Override + protected BufferObjectLayoutDefinition getDefinition() { + return null; + } +} diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java index 1ca96812..271c278f 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/SPDConstantBuffer.java @@ -20,11 +20,12 @@ public class SPDConstantBuffer extends BufferObject { BufferObjectLayoutEntry.ofVec2f().forGetter(SPDConstantBuffer::getInvInputSize).build() ); + public static final int SIZE = DEFINITION.size(BufferLayout.STD140); + /// The total number of mip levels SPD generates for each input texture slice. private int mips = 13; - /// The total number of dispatched work groups for one texture-array slice. SPD uses this to identify the last - /// work group, which downsamples the remaining mip levels. + /// number of thread groups per slice private int numWorkGroups; /// The offset of the first 64x64 input tile in work-group coordinates, normally `(left / 64, top / 64)` for a diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java index 7751c425..ed4f6ad0 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/query/GpuQueryOcclusionCuller.java @@ -58,6 +58,11 @@ public GpuQueryOcclusionCuller(ALRGpuDeviceExtension extension) { ); } + @Override + public void onResize(int newWidth, int newHeight) { + // Query-based culling has no size-dependent resources. + } + @Override public void beginFrame() { if (this.currentFrameState == null) { diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java index 8e5c9009..54289c19 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/util/MemoryAccess.java @@ -4,13 +4,22 @@ import sun.misc.Unsafe; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.function.LongPredicate; + +import static org.lwjgl.system.Pointer.BITS32; +import static org.lwjgl.system.jni.JNINativeInterface.NewDirectByteBuffer; /// @author IMS212 @SuppressWarnings("removal") public class MemoryAccess { private static final Unsafe UNSAFE = getUnsafe(); private static final boolean BITS32 = Pointer.BITS32; - + + private static final long ADDRESS = getAddressOffset(); + private static Unsafe getUnsafe() { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); @@ -21,6 +30,14 @@ private static Unsafe getUnsafe() { } } + public static long memAddress(ByteBuffer buffer) { + return buffer.position() + UNSAFE.getLong(buffer, ADDRESS); + } + + public static void memset(long address, long size, byte value) { + UNSAFE.setMemory(address, size, value); + } + public static void putInt(long address, int value) { UNSAFE.putInt(address, value); } @@ -76,4 +93,31 @@ public static long getAddress(long address) { return UNSAFE.getLong(address); } } + + private static long getFieldOffset(Class containerType, Class fieldType, LongPredicate predicate) { + Class c = containerType; + while (c != Object.class) { + Field[] fields = c.getDeclaredFields(); + for (Field field : fields) { + if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { + continue; + } + + long offset = UNSAFE.objectFieldOffset(field); + if (predicate.test(offset)) { + return offset; + } + } + c = c.getSuperclass(); + } + throw new UnsupportedOperationException("Failed to find field offset in class."); + } + + private static long getAddressOffset() { + long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL & (BITS32 ? 0xFFFF_FFFFL : 0xFFFF_FFFF_FFFF_FFFFL); + + ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0)); + + return getFieldOffset(bb.getClass(), long.class, offset -> UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS); + } } \ No newline at end of file diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh index 3d7dbaab..572c39ff 100644 --- a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/depth_convert.csh @@ -15,12 +15,12 @@ layout(binding = 2, r32f) writeonly uniform image2D Output; void main() { ivec2 idx = ivec2(gl_GlobalInvocationID.xy); + vec4 result; + if (idx.x >= uWidth || idx.y >= uHeight) { - imageStore(Output, idx, vec4(1, 1, 1, 1)); - return; + result = vec4(uPadValue, 0, 0, 1); + } else { + result = texelFetch(Input, idx, 0); } - - vec4 converted = texelFetch(Input, idx, 0); - - imageStore(Output, idx, converted); + imageStore(Output, idx, vec4(result.r, 0.0, 0.0, 1.0)); } \ No newline at end of file diff --git a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh index 295a3197..a371e4a6 100644 --- a/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh +++ b/module.rendering/src/main/resources/assets/anvillib_rendering/shaders/compute/ffx_spd_downsample_pass.csh @@ -86,8 +86,8 @@ vec2 InvInputSize() //layout (set = 0, binding = 1000) uniform sampler s_LinearClamp; //// SRVs //layout (set = 0, binding = 0) uniform texture2DArray r_input_downsample_src; - -layout (set = 0, binding = 1) uniform sampler2DArray r_input_downsample_src; +// in our usage case, those arrays only have one element, so replace it with a sampler2D +layout (set = 0, binding = 1) uniform sampler2D r_input_downsample_src; // UAV declarations // replace huge binding slot in original shader 2000 to 2 @@ -99,11 +99,11 @@ layout (set = 0, binding = 2, std430) coherent buffer rw_internal_global_atomic_ // replace huge binding slot in original shader 2001 to 3 // bind mip map 6 to this uniform // change format from rgba32f to r32f because we are handling depth texture -layout (set = 0, binding = 3, r32f) coherent uniform image2DArray rw_input_downsample_src_mid_mip; +layout (set = 0, binding = 3, r32f) coherent uniform image2D rw_input_downsample_src_mid_mip; // replace huge binding slot in original shader 2002 to 4 // change format from rgba32f to r32f because we are handling depth texture -layout (set = 0, binding = 4, r32f) uniform image2DArray rw_input_downsample_src_mips[SPD_MAX_MIP_LEVELS + 1]; +layout (set = 0, binding = 4, r32f) uniform image2D rw_input_downsample_src_mips[SPD_MAX_MIP_LEVELS + 1]; /// Compute an SRGB value from a linear value. /// @@ -146,34 +146,35 @@ uvec2 ffxRemapForWaveReduction(uint a) return uvec2(((a >> 2u) & 6u) | (a & 1u), ((a >> 3u) & 4u) | ((a >> 1u) & 3u)); } -vec4 SampleSrcImage(ivec2 uv, uint slice) +// removed slice because we are using image2D/sampler2D +vec4 SampleSrcImage(ivec2 uv) { vec2 textureCoord = vec2(uv) * InvInputSize() + InvInputSize(); // vec4 result = textureLod(sampler2DArray(r_input_downsample_src, s_LinearClamp), vec3(textureCoord, slice), 0); - vec4 result = textureLod(r_input_downsample_src, vec3(textureCoord, slice), 0); + vec4 result = textureLod(r_input_downsample_src, textureCoord, 0); // remove srgb convert because minecraft use linear rgb8 unorm // return vec4(ffxSrgbFromLinear(result.x), ffxSrgbFromLinear(result.y), ffxSrgbFromLinear(result.z), result.w); return result; } -vec4 LoadSrcImage(ivec2 uv, uint slice) +vec4 LoadSrcImage(ivec2 uv) { - return imageLoad(rw_input_downsample_src_mips[0], ivec3(uv, slice)); + return imageLoad(rw_input_downsample_src_mips[0], uv); } -void StoreSrcMip(vec4 value, ivec2 uv, uint slice, uint mip) +void StoreSrcMip(vec4 value, ivec2 uv, uint mip) { - imageStore(rw_input_downsample_src_mips[mip], ivec3(uv, slice), value); + imageStore(rw_input_downsample_src_mips[mip], uv, value); } -vec4 LoadMidMip(ivec2 uv, uint slice) +vec4 LoadMidMip(ivec2 uv) { - return imageLoad(rw_input_downsample_src_mid_mip, ivec3(uv, slice)); + return imageLoad(rw_input_downsample_src_mid_mip, uv); } -void StoreMidMip(vec4 value, ivec2 uv, uint slice) +void StoreMidMip(vec4 value, ivec2 uv) { - imageStore(rw_input_downsample_src_mid_mip, ivec3(uv, slice), value); + imageStore(rw_input_downsample_src_mid_mip, uv, value); } void IncreaseAtomicCounter(uint slice, inout uint counter) @@ -208,26 +209,26 @@ shared float spdIntermediateG[16][16]; shared float spdIntermediateB[16][16]; shared float spdIntermediateA[16][16]; -vec4 SpdLoadSourceImage(ivec2 tex, uint slice) +vec4 SpdLoadSourceImage(ivec2 tex) { #if defined SPD_LINEAR_SAMPLER - return SampleSrcImage(tex, slice); + return SampleSrcImage(tex); #else - return LoadSrcImage(tex, slice); + return LoadSrcImage(tex); #endif // SPD_LINEAR_SAMPLER } -vec4 SpdLoad(ivec2 tex, uint slice) +vec4 SpdLoad(ivec2 tex) { - return LoadMidMip(tex, slice); + return LoadMidMip(tex); } -void SpdStore(ivec2 pix, vec4 outValue, uint mip, uint slice) +void SpdStore(ivec2 pix, vec4 outValue, uint mip) { if (mip == 5) - StoreMidMip(outValue, pix, slice); + StoreMidMip(outValue, pix); else - StoreSrcMip(outValue, pix, slice, mip + 1); + StoreSrcMip(outValue, pix, mip + 1); } vec4 SpdLoadIntermediate(uint x, uint y) @@ -261,12 +262,12 @@ void ffxSpdWorkgroupShuffleBarrier() } // Only last active workgroup should proceed -bool SpdExitWorkgroup(uint numWorkGroups, uint localInvocationIndex, uint slice) +bool SpdExitWorkgroup(uint numWorkGroups, uint localInvocationIndex) { // global atomic counter if (localInvocationIndex == 0) { - SpdIncreaseAtomicCounter(slice); + SpdIncreaseAtomicCounter(0); } ffxSpdWorkgroupShuffleBarrier(); @@ -295,61 +296,61 @@ vec4 SpdReduceIntermediate(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3) return SpdReduce4(v0, v1, v2, v3); } -vec4 SpdReduceLoad4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3, uint slice) +vec4 SpdReduceLoad4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3) { - vec4 v0 = SpdLoad(ivec2(i0), slice); - vec4 v1 = SpdLoad(ivec2(i1), slice); - vec4 v2 = SpdLoad(ivec2(i2), slice); - vec4 v3 = SpdLoad(ivec2(i3), slice); + vec4 v0 = SpdLoad(ivec2(i0)); + vec4 v1 = SpdLoad(ivec2(i1)); + vec4 v2 = SpdLoad(ivec2(i2)); + vec4 v3 = SpdLoad(ivec2(i3)); return SpdReduce4(v0, v1, v2, v3); } -vec4 SpdReduceLoad4(uvec2 base, uint slice) +vec4 SpdReduceLoad4(uvec2 base) { - return SpdReduceLoad4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1), slice); + return SpdReduceLoad4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1)); } -vec4 SpdReduceLoadSourceImage4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3, uint slice) +vec4 SpdReduceLoadSourceImage4(uvec2 i0, uvec2 i1, uvec2 i2, uvec2 i3) { - vec4 v0 = SpdLoadSourceImage(ivec2(i0), slice); - vec4 v1 = SpdLoadSourceImage(ivec2(i1), slice); - vec4 v2 = SpdLoadSourceImage(ivec2(i2), slice); - vec4 v3 = SpdLoadSourceImage(ivec2(i3), slice); + vec4 v0 = SpdLoadSourceImage(ivec2(i0)); + vec4 v1 = SpdLoadSourceImage(ivec2(i1)); + vec4 v2 = SpdLoadSourceImage(ivec2(i2)); + vec4 v3 = SpdLoadSourceImage(ivec2(i3)); return SpdReduce4(v0, v1, v2, v3); } -vec4 SpdReduceLoadSourceImage(uvec2 base, uint slice) +vec4 SpdReduceLoadSourceImage(uvec2 base) { #if defined(SPD_LINEAR_SAMPLER) - return SpdLoadSourceImage(ivec2(base), slice); + return SpdLoadSourceImage(ivec2(base)); #else - return SpdReduceLoadSourceImage4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1), slice); + return SpdReduceLoadSourceImage4(base + uvec2(0, 0), base + uvec2(0, 1), base + uvec2(1, 0), base + uvec2(1, 1)); #endif } -void SpdDownsampleMips_0_1_Intrinsics(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMips_0_1_Intrinsics(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { vec4 v[4]; ivec2 tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2); ivec2 pix = ivec2(workGroupID.xy * 32) + ivec2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); + v[0] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[0], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2); pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); + v[1] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[1], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2 + 32); pix = ivec2(workGroupID.xy * 32) + ivec2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); + v[2] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[2], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2 + 32); pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); + v[3] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[3], 0); if (mip <= 1) return; @@ -361,43 +362,43 @@ void SpdDownsampleMips_0_1_Intrinsics(uint x, uint y, uvec2 workGroupID, uint lo if ((localInvocationIndex % 4) == 0) { - SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2), v[0], 1, slice); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2), v[0], 1); SpdStoreIntermediate(x / 2, y / 2, v[0]); - SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2), v[1], 1, slice); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2), v[1], 1); SpdStoreIntermediate(x / 2 + 8, y / 2, v[1]); - SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2 + 8), v[2], 1, slice); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2, y / 2 + 8), v[2], 1); SpdStoreIntermediate(x / 2, y / 2 + 8, v[2]); - SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2 + 8), v[3], 1, slice); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x / 2 + 8, y / 2 + 8), v[3], 1); SpdStoreIntermediate(x / 2 + 8, y / 2 + 8, v[3]); } } -void SpdDownsampleMips_0_1_LDS(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMips_0_1_LDS(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { vec4 v[4]; ivec2 tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2); ivec2 pix = ivec2(workGroupID.xy * 32) + ivec2(x, y); - v[0] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[0], 0, slice); + v[0] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[0], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2); pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y); - v[1] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[1], 0, slice); + v[1] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[1], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2, y * 2 + 32); pix = ivec2(workGroupID.xy * 32) + ivec2(x, y + 16); - v[2] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[2], 0, slice); + v[2] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[2], 0); tex = ivec2(workGroupID.xy * 64) + ivec2(x * 2 + 32, y * 2 + 32); pix = ivec2(workGroupID.xy * 32) + ivec2(x + 16, y + 16); - v[3] = SpdReduceLoadSourceImage(tex, slice); - SpdStore(pix, v[3], 0, slice); + v[3] = SpdReduceLoadSourceImage(tex); + SpdStore(pix, v[3], 0); if (mip <= 1) return; @@ -409,7 +410,7 @@ void SpdDownsampleMips_0_1_LDS(uint x, uint y, uvec2 workGroupID, uint localInvo if (localInvocationIndex < 64) { v[i] = SpdReduceIntermediate(uvec2(x * 2 + 0, y * 2 + 0), uvec2(x * 2 + 1, y * 2 + 0), uvec2(x * 2 + 0, y * 2 + 1), uvec2(x * 2 + 1, y * 2 + 1)); - SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1, slice); + SpdStore(ivec2(workGroupID.xy * 16) + ivec2(x + (i % 2) * 8, y + (i / 2) * 8), v[i], 1); } ffxSpdWorkgroupShuffleBarrier(); } @@ -423,22 +424,22 @@ void SpdDownsampleMips_0_1_LDS(uint x, uint y, uvec2 workGroupID, uint localInvo } } -void SpdDownsampleMips_0_1(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMips_0_1(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { #if defined(FFX_SPD_NO_WAVE_OPERATIONS) - SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip, slice); + SpdDownsampleMips_0_1_LDS(x, y, workGroupID, localInvocationIndex, mip); #else - SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip, slice); + SpdDownsampleMips_0_1_Intrinsics(x, y, workGroupID, localInvocationIndex, mip); #endif } -void SpdDownsampleMip_2(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMip_2(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { #if defined(FFX_SPD_NO_WAVE_OPERATIONS) if (localInvocationIndex < 64) { vec4 v = SpdReduceIntermediate(uvec2(x * 2 + 0, y * 2 + 0), uvec2(x * 2 + 1, y * 2 + 0), uvec2(x * 2 + 0, y * 2 + 1), uvec2(x * 2 + 1, y * 2 + 1)); - SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x, y), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x, y), v, mip); // store to LDS, try to reduce bank conflicts // x 0 x 0 x 0 x 0 x 0 x 0 x 0 x 0 // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -455,13 +456,13 @@ void SpdDownsampleMip_2(uint x, uint y, uvec2 workGroupID, uint localInvocationI // quad index 0 stores result if (localInvocationIndex % 4 == 0) { - SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 8) + ivec2(x / 2, y / 2), v, mip); SpdStoreIntermediate(x + (y / 2) % 2, y, v); } #endif } -void SpdDownsampleMip_3(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMip_3(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { #if defined(FFX_SPD_NO_WAVE_OPERATIONS) if (localInvocationIndex < 16) @@ -471,7 +472,7 @@ void SpdDownsampleMip_3(uint x, uint y, uvec2 workGroupID, uint localInvocationI // 0 x 0 x // 0 0 0 0 vec4 v = SpdReduceIntermediate(uvec2(x * 4 + 0 + 0, y * 4 + 0), uvec2(x * 4 + 2 + 0, y * 4 + 0), uvec2(x * 4 + 0 + 1, y * 4 + 2), uvec2(x * 4 + 2 + 1, y * 4 + 2)); - SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x, y), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x, y), v, mip); // store to LDS // x 0 0 0 x 0 0 0 x 0 0 0 x 0 0 0 // 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 @@ -493,14 +494,14 @@ void SpdDownsampleMip_3(uint x, uint y, uvec2 workGroupID, uint localInvocationI // quad index 0 stores result if (localInvocationIndex % 4 == 0) { - SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 4) + ivec2(x / 2, y / 2), v, mip); SpdStoreIntermediate(x * 2 + y / 2, y * 2, v); } } #endif } -void SpdDownsampleMip_4(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMip_4(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint mip) { #if defined(FFX_SPD_NO_WAVE_OPERATIONS) if (localInvocationIndex < 4) @@ -512,7 +513,7 @@ void SpdDownsampleMip_4(uint x, uint y, uvec2 workGroupID, uint localInvocationI uvec2(x * 8 + 4 + 0 + y * 2, y * 8 + 0), uvec2(x * 8 + 0 + 1 + y * 2, y * 8 + 4), uvec2(x * 8 + 4 + 1 + y * 2, y * 8 + 4)); - SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x, y), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x, y), v, mip); // store to LDS // x x x x 0 ... // 0 ... @@ -526,14 +527,14 @@ void SpdDownsampleMip_4(uint x, uint y, uvec2 workGroupID, uint localInvocationI // quad index 0 stores result if (localInvocationIndex % 4 == 0) { - SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x / 2, y / 2), v, mip, slice); + SpdStore(ivec2(workGroupID.xy * 2) + ivec2(x / 2, y / 2), v, mip); SpdStoreIntermediate(x / 2 + y, 0, v); } } #endif } -void SpdDownsampleMip_5(uvec2 workGroupID, uint localInvocationIndex, uint mip, uint slice) +void SpdDownsampleMip_5(uvec2 workGroupID, uint localInvocationIndex, uint mip) { #if defined(FFX_SPD_NO_WAVE_OPERATIONS) if (localInvocationIndex < 1) @@ -541,7 +542,7 @@ void SpdDownsampleMip_5(uvec2 workGroupID, uint localInvocationIndex, uint mip, // x x x x 0 ... // 0 ... vec4 v = SpdReduceIntermediate(uvec2(0, 0), uvec2(1, 0), uvec2(2, 0), uvec2(3, 0)); - SpdStore(ivec2(workGroupID.xy), v, mip, slice); + SpdStore(ivec2(workGroupID.xy), v, mip); } #else if (localInvocationIndex < 4) @@ -551,64 +552,64 @@ void SpdDownsampleMip_5(uvec2 workGroupID, uint localInvocationIndex, uint mip, // quad index 0 stores result if (localInvocationIndex % 4 == 0) { - SpdStore(ivec2(workGroupID.xy), v, mip, slice); + SpdStore(ivec2(workGroupID.xy), v, mip); } } #endif } -void SpdDownsampleMips_6_7(uint x, uint y, uint mips, uint slice) +void SpdDownsampleMips_6_7(uint x, uint y, uint mips) { ivec2 tex = ivec2(x * 4 + 0, y * 4 + 0); ivec2 pix = ivec2(x * 2 + 0, y * 2 + 0); - vec4 v0 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v0, 6, slice); + vec4 v0 = SpdReduceLoad4(tex); + SpdStore(pix, v0, 6); tex = ivec2(x * 4 + 2, y * 4 + 0); pix = ivec2(x * 2 + 1, y * 2 + 0); - vec4 v1 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v1, 6, slice); + vec4 v1 = SpdReduceLoad4(tex); + SpdStore(pix, v1, 6); tex = ivec2(x * 4 + 0, y * 4 + 2); pix = ivec2(x * 2 + 0, y * 2 + 1); - vec4 v2 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v2, 6, slice); + vec4 v2 = SpdReduceLoad4(tex); + SpdStore(pix, v2, 6); tex = ivec2(x * 4 + 2, y * 4 + 2); pix = ivec2(x * 2 + 1, y * 2 + 1); - vec4 v3 = SpdReduceLoad4(tex, slice); - SpdStore(pix, v3, 6, slice); + vec4 v3 = SpdReduceLoad4(tex); + SpdStore(pix, v3, 6); if (mips <= 7) return; // no barrier needed, working on values only from the same thread vec4 v = SpdReduce4(v0, v1, v2, v3); - SpdStore(ivec2(x, y), v, 7, slice); + SpdStore(ivec2(x, y), v, 7); SpdStoreIntermediate(x, y, v); } -void SpdDownsampleNextFour(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint baseMip, uint mips, uint slice) +void SpdDownsampleNextFour(uint x, uint y, uvec2 workGroupID, uint localInvocationIndex, uint baseMip, uint mips) { if (mips <= baseMip) return; ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip, slice); + SpdDownsampleMip_2(x, y, workGroupID, localInvocationIndex, baseMip); if (mips <= baseMip + 1) return; ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1, slice); + SpdDownsampleMip_3(x, y, workGroupID, localInvocationIndex, baseMip + 1); if (mips <= baseMip + 2) return; ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2, slice); + SpdDownsampleMip_4(x, y, workGroupID, localInvocationIndex, baseMip + 2); if (mips <= baseMip + 3) return; ffxSpdWorkgroupShuffleBarrier(); - SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3, slice); + SpdDownsampleMip_5(workGroupID, localInvocationIndex, baseMip + 3); } /// Downsamples a 64x64 tile based on the work group id. @@ -621,34 +622,34 @@ void SpdDownsampleNextFour(uint x, uint y, uvec2 workGroupID, uint localInvocati /// @param [in] slice the slice of the input texture /// /// @ingroup FfxGPUSpd -void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups, uint slice) +void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups) { // compute MIP level 0 and 1 uvec2 sub_xy = ffxRemapForWaveReduction(localInvocationIndex % 64); uint x = sub_xy.x + 8 * ((localInvocationIndex >> 6) % 2); uint y = sub_xy.y + 8 * (localInvocationIndex >> 7); - SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips, slice); + SpdDownsampleMips_0_1(x, y, workGroupID, localInvocationIndex, mips); // compute MIP level 2, 3, 4, 5 - SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips, slice); + SpdDownsampleNextFour(x, y, workGroupID, localInvocationIndex, 2, mips); if (mips <= 6) return; // increase the global atomic counter for the given slice and check if it's the last remaining thread group: // terminate if not, continue if yes. - if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex, slice)) + if (SpdExitWorkgroup(numWorkGroups, localInvocationIndex)) return; // reset the global atomic counter back to 0 for the next spd dispatch - SpdResetAtomicCounter(slice); + SpdResetAtomicCounter(0); // After mip 5 there is only a single workgroup left that downsamples the remaining up to 64x64 texels. // compute MIP level 6 and 7 - SpdDownsampleMips_6_7(x, y, mips, slice); + SpdDownsampleMips_6_7(x, y, mips); // compute MIP level 8, 9, 10, 11 - SpdDownsampleNextFour(x, y, uvec2(0, 0), localInvocationIndex, 8, mips, slice); + SpdDownsampleNextFour(x, y, uvec2(0, 0), localInvocationIndex, 8, mips); } /// Downsamples a 64x64 tile based on the work group id and work group offset. @@ -662,14 +663,14 @@ void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint /// @param [in] workGroupOffset the work group offset. it's (0,0) in case the entire input texture is downsampled. /// /// @ingroup FfxGPUSpd -void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups, uint slice, uvec2 workGroupOffset) +void SpdDownsample(uvec2 workGroupID, uint localInvocationIndex, uint mips, uint numWorkGroups, uvec2 workGroupOffset) { - SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups, slice); + SpdDownsample(workGroupID + workGroupOffset, localInvocationIndex, mips, numWorkGroups); } void DOWNSAMPLE(uint localThreadId, uvec3 workGroupId) { - SpdDownsample(workGroupId.xy, localThreadId, Mips(), NumWorkGroups(), workGroupId.z, WorkGroupOffset()); + SpdDownsample(workGroupId.xy, localThreadId, Mips(), NumWorkGroups(), WorkGroupOffset()); } layout (local_size_x = 256, local_size_y = 1, local_size_z = 1) in;