[26.3] Fix various issues with stencil support (#3510)

Co-authored-by: RogueLogix <[email protected]>
This commit is contained in:
Dennis C
2026-10-10 23:34:12 +02:00
committed by GitHub
co-authored by RogueLogix
parent d4d885d12b
commit 918f55e357
10 changed files with 141 additions and 205 deletions
@@ -0,0 +1,23 @@
--- a/com/mojang/blaze3d/resource/RenderTargetDescriptor.java
+++ b/com/mojang/blaze3d/resource/RenderTargetDescriptor.java
@@ -20,14 +_,18 @@
}
public void prepare(RenderTarget resource) {
- if (this.color != null && this.depth != null && this.color.clearColor != null && this.depth.clearColor != null) {
+ if (this.color != null && this.depth != null && this.color.clearColor != null && this.depth.clearColor != null && this.depth.format.hasDepthAspect()) {
RenderSystem.getDevice()
.createCommandEncoder()
.clearColorAndDepthTextures(resource.getColorTexture(), this.color.clearColor, resource.getDepthTexture(), this.depth.clearColor.x());
} else if (this.color != null && this.color.clearColor != null) {
RenderSystem.getDevice().createCommandEncoder().clearColorTexture(resource.getColorTexture(), this.color.clearColor);
- } else if (this.depth != null && this.depth.clearColor != null) {
+ } else if (this.depth != null && this.depth.clearColor != null && this.depth.format.hasDepthAspect()) {
RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(resource.getDepthTexture(), this.depth.clearColor.x());
+ }
+ if (this.depth != null && this.depth.clearColor != null && this.depth.format.hasStencilAspect()) {
+ int value = (int) (this.depth.format.hasDepthAspect() ? this.depth.clearColor.y() : this.depth.clearColor.x());
+ RenderSystem.getDevice().createCommandEncoder().clearStencilTexture(resource.getDepthTexture(), value);
}
}
@@ -5,38 +5,34 @@
public abstract int createFrameBufferObject();
- public abstract void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot);
+ public abstract void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, boolean useStencil);
+ public abstract void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, int stencil, int stencilMipLevel);
+
+ public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot) {
+ this.bindFrameBufferTextures(fbo, color, colorMipLevels, depth, depthMipLevel, bindSlot, false);
+ this.bindFrameBufferTextures(fbo, color, colorMipLevels, depth, depthMipLevel, bindSlot, 0, 0);
+ }
public void bindFrameBufferTextures(int fbo, int color, int depth, int mipLevel, int bindSlot) {
- this.bindFrameBufferTextures(fbo, new int[]{color}, new int[]{mipLevel}, depth, mipLevel, bindSlot);
+ this.bindFrameBufferTextures(fbo, color, depth, mipLevel, bindSlot, false);
+ this.bindFrameBufferTextures(fbo, color, depth, mipLevel, bindSlot, 0, 0);
+ }
+
+ public void bindFrameBufferTextures(int fbo, int color, int depth, int mipLevel, int bindSlot, boolean useStencil) {
+ this.bindFrameBufferTextures(fbo, new int[]{color}, new int[]{mipLevel}, depth, mipLevel, bindSlot, useStencil);
+ public void bindFrameBufferTextures(int fbo, int color, int depth, int mipLevel, int bindSlot, int stencil, int stencilMipLevel) {
+ this.bindFrameBufferTextures(fbo, new int[]{color}, new int[]{mipLevel}, depth, mipLevel, bindSlot, stencil, stencilMipLevel);
}
public abstract void blitFrameBuffers(
@@ -102,12 +_,17 @@
@@ -102,12 +_,13 @@
}
@Override
- public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot) {
+ public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, boolean useStencil) {
+ public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, int stencil, int stencilMipLevel) {
for (int i = 0; i < color.length; i++) {
ARBDirectStateAccess.glNamedFramebufferTexture(fbo, 36064 + i, color[i], colorMipLevels[i]);
}
ARBDirectStateAccess.glNamedFramebufferTexture(fbo, 36096, depth, depthMipLevel);
+ if (useStencil) {
+ ARBDirectStateAccess.glNamedFramebufferTexture(fbo, GL33C.GL_STENCIL_ATTACHMENT, depth, depthMipLevel);
+ } else {
+ ARBDirectStateAccess.glNamedFramebufferTexture(fbo, GL33C.GL_STENCIL_ATTACHMENT, 0, 0);
+ }
+ ARBDirectStateAccess.glNamedFramebufferTexture(fbo, GL33C.GL_STENCIL_ATTACHMENT, stencil, stencilMipLevel);
if (bindSlot != 0) {
GlStateManager._glBindFramebuffer(bindSlot, fbo);
}
@@ -45,19 +41,15 @@
@Override
- public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot) {
+ public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, boolean useStencil) {
+ public void bindFrameBufferTextures(int fbo, int[] color, int[] colorMipLevels, int depth, int depthMipLevel, int bindSlot, int stencil, int stencilMipLevel) {
int tempBindSlot = bindSlot == 0 ? 36009 : bindSlot;
int oldFbo = GlStateManager.getFrameBuffer(tempBindSlot);
GlStateManager._glBindFramebuffer(tempBindSlot, fbo);
@@ -227,6 +_,11 @@
@@ -227,6 +_,7 @@
}
GlStateManager._glFramebufferTexture2D(tempBindSlot, 36096, 3553, depth, depthMipLevel);
+ if (useStencil) {
+ GlStateManager._glFramebufferTexture2D(tempBindSlot, GL33C.GL_STENCIL_ATTACHMENT, 3553, depth, depthMipLevel);
+ } else {
+ GlStateManager._glFramebufferTexture2D(tempBindSlot, GL33C.GL_STENCIL_ATTACHMENT, 3553, 0, 0);
+ }
+ GlStateManager._glFramebufferTexture2D(tempBindSlot, GL33C.GL_STENCIL_ATTACHMENT, 3553, stencil, stencilMipLevel);
if (bindSlot == 0) {
GlStateManager._glBindFramebuffer(tempBindSlot, oldFbo);
}
@@ -1,11 +1,10 @@
--- a/com/mojang/renderpearl/backend/opengl/FrameBufferCache.java
+++ b/com/mojang/renderpearl/backend/opengl/FrameBufferCache.java
@@ -51,7 +_,7 @@
}
@@ -52,6 +_,7 @@
dsa.bindFrameBufferTextures(
- fbo, colorIds, mipLevels, depthAttachment == null ? 0 : depthAttachment.glId(), depthAttachment == null ? 0 : depthAttachment.fboMipLevel(), 0
+ fbo, colorIds, mipLevels, depthAttachment == null ? 0 : depthAttachment.glId(), depthAttachment == null ? 0 : depthAttachment.fboMipLevel(), 0, depthAttachment != null && depthAttachment.hasStencilAspect()
fbo, colorIds, mipLevels, depthAttachment == null ? 0 : depthAttachment.glId(), depthAttachment == null ? 0 : depthAttachment.fboMipLevel(), 0
+ , depthAttachment != null && depthAttachment.hasStencilAspect() ? depthAttachment.glId() : 0, depthAttachment != null && depthAttachment.hasStencilAspect() ? depthAttachment.fboMipLevel() : 0
);
return fbo;
}
@@ -1,27 +1,23 @@
--- a/com/mojang/renderpearl/backend/opengl/GlCommandEncoder.java
+++ b/com/mojang/renderpearl/backend/opengl/GlCommandEncoder.java
@@ -262,8 +_,9 @@
GlStateManager._depthMask(true);
GlStateManager._disableScissorTest();
+ boolean hasStencil = depthTexture.getFormat().hasStencilAspect();
for (int i = 0; i < depthTexture.getMipLevels(); i++) {
- this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, 0, ((GlTexture)depthTexture).id, i, 36160);
+ this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, 0, ((GlTexture)depthTexture).id, i, 36160, hasStencil);
GL33C.glDrawBuffer(0);
GlStateManager._clear(256);
}
@@ -274,6 +_,17 @@
@@ -274,6 +_,24 @@
}
@Override
+ public void clearStencilTexture(GpuTexture texture, int value) {
+ this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, 0, ((GlTexture)texture).id, 0, GlConst.GL_FRAMEBUFFER, true);
+ GL33C.glDrawBuffer(GlConst.GL_NONE);
+ GL33C.glClearStencil(value);
+ GlStateManager._stencilMask(-1);
+ GlStateManager._depthMask(true);
+ GlStateManager._clear(GL33C.GL_STENCIL_BUFFER_BIT);
+ GlStateManager._disableScissorTest();
+
+ for (int i = 0; i < texture.getMipLevels(); i++) {
+ this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, 0, 0, 0, GlConst.GL_FRAMEBUFFER, ((GlTexture)texture).id, i);
+ GL33C.glDrawBuffer(GlConst.GL_NONE);
+ GlStateManager._clear(GL33C.GL_STENCIL_BUFFER_BIT);
+ }
+
+ GL33C.glDrawBuffer(GlConst.GL_COLOR_ATTACHMENT0);
+ GlStateManager._glFramebufferTexture2D(GlConst.GL_FRAMEBUFFER, GL33C.GL_STENCIL_ATTACHMENT, GlConst.GL_TEXTURE_2D, 0, 0);
+ GlStateManager._glBindFramebuffer(GlConst.GL_FRAMEBUFFER, 0);
+ }
+
@@ -36,8 +32,8 @@
- this.device.directStateAccess().bindFrameBufferTextures(this.readFbo, isDepth ? 0 : sourceId, isDepth ? sourceId : 0, mipLevel, 0);
- this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, isDepth ? 0 : destId, isDepth ? destId : 0, mipLevel, 0);
+ boolean hasStencil = source.getFormat().hasStencilAspect();
+ this.device.directStateAccess().bindFrameBufferTextures(this.readFbo, isDepth ? 0 : sourceId, isDepth ? sourceId : 0, 0, 0, hasStencil);
+ this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, isDepth ? 0 : destId, isDepth ? destId : 0, 0, 0, hasStencil);
+ this.device.directStateAccess().bindFrameBufferTextures(this.readFbo, (isDepth || hasStencil) ? 0 : sourceId, isDepth ? sourceId : 0, 0, 0, hasStencil ? sourceId : 0, 0);
+ this.device.directStateAccess().bindFrameBufferTextures(this.drawFbo, (isDepth || hasStencil) ? 0 : destId, isDepth ? destId : 0, 0, 0, hasStencil ? destId : 0, 0);
+ int bufferMask = 0;
+ if (source.getFormat().hasColorAspect()) {
+ bufferMask |= GlConst.GL_COLOR_BUFFER_BIT;
@@ -1,8 +1,10 @@
--- a/com/mojang/renderpearl/backend/vulkan/VulkanCommandEncoder.java
+++ b/com/mojang/renderpearl/backend/vulkan/VulkanCommandEncoder.java
@@ -320,11 +_,21 @@
@@ -319,12 +_,24 @@
depthAttachmentInfo.loadOp(0);
}
+ if (depthAttachment.textureView().texture().getFormat().hasDepthAspect())
renderingInfo.pDepthAttachment(depthAttachmentInfo);
+
+ if (vulkanDepthAttachment.texture().getFormat().hasStencilAspect()) {
@@ -16,10 +18,11 @@
}
KHRDynamicRendering.vkCmdBeginRenderingKHR(this.commandBuffer(), renderingInfo);
+ boolean hasDepth = depthAttachment != null && depthAttachment.textureView().texture().getFormat().hasDepthAspect();
+ boolean hasStencil = depthAttachment != null && depthAttachment.textureView().texture().getFormat().hasStencilAspect();
this.currentRenderPass = new VulkanRenderPass(
- this.device, this, this.commandBuffer(), this.checkpointStorage, renderArea, width, height, depthAttachment != null, descriptor.label()
+ this.device, this, this.commandBuffer(), this.checkpointStorage, renderArea, width, height, depthAttachment != null, hasStencil, descriptor.label()
+ this.device, this, this.commandBuffer(), this.checkpointStorage, renderArea, width, height, hasDepth, hasStencil, descriptor.label()
);
}
@@ -0,0 +1,12 @@
--- a/com/mojang/renderpearl/backend/vulkan/VulkanGpuTexture.java
+++ b/com/mojang/renderpearl/backend/vulkan/VulkanGpuTexture.java
@@ -63,7 +_,8 @@
barrier.dstQueueFamilyIndex(-1);
barrier.image(this.vkImage);
VkImageSubresourceRange subresourceRange = barrier.subresourceRange();
- subresourceRange.aspectMask(this.getFormat().hasColorAspect() ? 1 : 2);
+ // Neo: respect stencil aspect in aspect mask
+ subresourceRange.aspectMask(VulkanConst.formatAspectMask(this.getFormat()));
subresourceRange.baseMipLevel(0);
subresourceRange.levelCount(this.getMipLevels());
subresourceRange.baseArrayLayer(0);
@@ -0,0 +1,12 @@
--- a/com/mojang/renderpearl/backend/vulkan/VulkanGpuTextureView.java
+++ b/com/mojang/renderpearl/backend/vulkan/VulkanGpuTextureView.java
@@ -26,7 +_,8 @@
imageViewCreateInfo.viewType(isCubemap ? 3 : 1);
imageViewCreateInfo.format(VulkanConst.toVk(texture.getFormat()));
VkImageSubresourceRange subresourceRange = imageViewCreateInfo.subresourceRange();
- subresourceRange.aspectMask(texture.getFormat().hasColorAspect() ? 1 : 2);
+ // Neo: respect stencil aspect in aspect mask
+ subresourceRange.aspectMask(VulkanConst.formatAspectMask(texture.getFormat()));
subresourceRange.baseMipLevel(baseMipLevel);
subresourceRange.levelCount(mipLevels);
subresourceRange.baseArrayLayer(0);
@@ -29,7 +29,7 @@
this.uniforms.clear();
this.uniforms.size(vulkanRenderPipeline.uniforms().size());
+ if (this.hasStencil) {
+ VK12.vkCmdBindPipeline(this.commandBuffer(), VK12.VK_PIPELINE_BIND_POINT_GRAPHICS, this.pipeline.withDepthStencilPipeline());
+ VK12.vkCmdBindPipeline(this.commandBuffer(), VK12.VK_PIPELINE_BIND_POINT_GRAPHICS, this.hasDepth ? this.pipeline.withDepthStencilPipeline() : this.pipeline.withStencilPipeline());
+ return;
+ }
VK12.vkCmdBindPipeline(this.commandBuffer(), 0, this.hasDepth ? this.pipeline.withDepthPipeline() : this.pipeline.withoutDepthPipeline());
@@ -1,25 +1,28 @@
--- a/com/mojang/renderpearl/backend/vulkan/VulkanRenderPipeline.java
+++ b/com/mojang/renderpearl/backend/vulkan/VulkanRenderPipeline.java
@@ -41,6 +_,7 @@
@@ -41,6 +_,8 @@
@OnlyIn(Dist.CLIENT)
public final class VulkanRenderPipeline implements BackendRenderPipeline, Destroyable {
private final VulkanDevice device;
+ private final long withStencilPipeline;
+ private final long withDepthStencilPipeline;
private final long withDepthPipeline;
private final long withoutDepthPipeline;
private final long pipelineLayout;
@@ -51,6 +_,7 @@
@@ -51,6 +_,8 @@
public VulkanRenderPipeline(
VulkanDevice device,
+ long withStencilPipeline,
+ long withDepthStencilPipeline,
long withDepthPipeline,
long withoutDepthPipeline,
long pipelineLayout,
@@ -59,6 +_,7 @@
@@ -59,6 +_,8 @@
List<BindGroupLayout.UniformDescription> uniforms
) {
this.device = device;
+ this.withStencilPipeline = withStencilPipeline;
+ this.withDepthStencilPipeline = withDepthStencilPipeline;
this.withDepthPipeline = withDepthPipeline;
this.withoutDepthPipeline = withoutDepthPipeline;
@@ -52,40 +55,47 @@
}
List<ColorTargetState> colorTargetStates = pipelineCreateInfo.colorTargetStates();
@@ -289,6 +_,7 @@
@@ -289,7 +_,7 @@
long withDepthPipeline = pointer.get(0);
device.instance().debug().setObjectName(device.vkDevice(), 19, withDepthPipeline, () -> "Pipeline " + pipelineCreateInfo.name());
long withoutDepthPipeline;
+ long withDepthStencilPipeline = 0L;
if (depthStencilState == null) {
- if (depthStencilState == null) {
+ if (true) {
renderingInfo.depthAttachmentFormat(0);
VulkanUtils.crashIfFailure(
@@ -300,10 +_,18 @@
device.instance().debug().setObjectName(device.vkDevice(), 19, withoutDepthPipeline, () -> "Pipeline " + pipelineCreateInfo.name());
} else {
device,
@@ -302,8 +_,20 @@
withoutDepthPipeline = 0L;
+
+ if (depthStencilState.stencilTest() != null) {
+ renderingInfo.depthAttachmentFormat(VK12.VK_FORMAT_D32_SFLOAT_S8_UINT);
+ renderingInfo.stencilAttachmentFormat(VK12.VK_FORMAT_D32_SFLOAT_S8_UINT);
+ VulkanUtils.crashIfFailure(device, VK12.vkCreateGraphicsPipelines(device.vkDevice(), 0L, createInfo, null, pointer), "Can't compile pipeline " + pipelineCreateInfo.name());
+ withDepthStencilPipeline = pointer.get(0);
+ device.instance().debug().setObjectName(device.vkDevice(), VK12.VK_OBJECT_TYPE_PIPELINE, withDepthStencilPipeline, () -> "Pipeline " + pipelineCreateInfo.name());
+ }
}
+ renderingInfo.depthAttachmentFormat(VK12.VK_FORMAT_D32_SFLOAT_S8_UINT);
+ renderingInfo.stencilAttachmentFormat(VK12.VK_FORMAT_D32_SFLOAT_S8_UINT);
+ VulkanUtils.crashIfFailure(device, VK12.vkCreateGraphicsPipelines(device.vkDevice(), 0L, createInfo, null, pointer), "Can't compile pipeline " + pipelineCreateInfo.name());
+ long withDepthStencilPipeline = pointer.get(0);
+ device.instance().debug().setObjectName(device.vkDevice(), VK12.VK_OBJECT_TYPE_PIPELINE, withDepthStencilPipeline, () -> "Pipeline " + pipelineCreateInfo.name());
+
+ renderingInfo.depthAttachmentFormat(VK12.VK_FORMAT_UNDEFINED);
+ renderingInfo.stencilAttachmentFormat(VK12.VK_FORMAT_S8_UINT);
+ VulkanUtils.crashIfFailure(device, VK12.vkCreateGraphicsPipelines(device.vkDevice(), 0L, createInfo, null, pointer), "Can't compile pipeline " + pipelineCreateInfo.name());
+ long withStencilPipeline = pointer.get(0);
+ device.instance().debug().setObjectName(device.vkDevice(), VK12.VK_OBJECT_TYPE_PIPELINE, withDepthStencilPipeline, () -> "Pipeline " + pipelineCreateInfo.name());
+
return new VulkanRenderPipeline(
- device, withDepthPipeline, withoutDepthPipeline, pipelineLayout, descriptorSetLayout, compiledShaderModules, pipelineCreateInfo.uniforms()
+ device, withDepthStencilPipeline, withDepthPipeline, withoutDepthPipeline, pipelineLayout, descriptorSetLayout, compiledShaderModules, pipelineCreateInfo.uniforms()
+ device, withStencilPipeline, withDepthStencilPipeline, withDepthPipeline, withoutDepthPipeline, pipelineLayout, descriptorSetLayout, compiledShaderModules, pipelineCreateInfo.uniforms()
);
}
}
@@ -334,6 +_,10 @@
@@ -334,6 +_,14 @@
public VulkanDevice device() {
return this.device;
+ }
+
+ public long withStencilPipeline() {
+ return this.withStencilPipeline;
+ }
+
+ public long withDepthStencilPipeline() {
+ return this.withDepthStencilPipeline;
}
@@ -5,38 +5,21 @@
package net.neoforged.neoforge.oldtest.client.rendering;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.renderpearl.api.pipeline.CompareOp;
import com.mojang.renderpearl.api.pipeline.DepthStencilState;
import com.mojang.renderpearl.api.pipeline.RenderPipeline;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.render.pip.PictureInPictureRenderer;
import net.minecraft.client.renderer.SubmitNodeCollector;
import net.minecraft.client.renderer.feature.FeatureRenderDispatcher;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
import net.minecraft.client.renderer.state.gui.pip.PictureInPictureRenderState;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.LightCoordsUtil;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.client.event.ConfigureMainRenderTargetEvent;
import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent;
import net.neoforged.neoforge.client.event.RegisterPictureInPictureRenderersEvent;
import net.neoforged.neoforge.client.pipeline.PipelineModifier;
import net.neoforged.neoforge.client.pipeline.RegisterPipelineModifiersEvent;
import net.neoforged.neoforge.client.stencil.StencilOperation;
import net.neoforged.neoforge.client.stencil.StencilPerFaceTest;
import net.neoforged.neoforge.client.stencil.StencilTest;
import org.jspecify.annotations.Nullable;
/**
* Basic test that uses the stencil buffer.
@@ -60,8 +43,31 @@ public class StencilEnableTest {
private static final State ENABLED = State.ENABLE_REGISTRATION;
private static final ResourceKey<PipelineModifier> STENCIL_FILL_KEY = ResourceKey.create(PipelineModifier.MODIFIERS_KEY, Identifier.fromNamespaceAndPath(MOD_ID, "stencil_fill"));
private static final ResourceKey<PipelineModifier> STENCIL_APPLY_KEY = ResourceKey.create(PipelineModifier.MODIFIERS_KEY, Identifier.fromNamespaceAndPath(MOD_ID, "stencil_apply"));
private static final RenderPipeline GUI_TEXTURED_STENCIL_FILL = RenderPipelines.GUI_TEXTURED.toBuilder()
.withLocation(Identifier.fromNamespaceAndPath(MOD_ID, "pipeline/gui_textured_stencil_fill"))
.withDepthStencilState(new DepthStencilState(CompareOp.GREATER_THAN_OR_EQUAL, true, 0F, 0F, new StencilTest(
new StencilPerFaceTest(
StencilOperation.KEEP,
StencilOperation.KEEP,
StencilOperation.REPLACE,
CompareOp.ALWAYS_PASS),
0xFF,
0xFF,
1)))
.build();
private static final RenderPipeline GUI_STENCIL_APPLY = RenderPipelines.GUI.toBuilder()
.withLocation(Identifier.fromNamespaceAndPath(MOD_ID, "pipeline/gui_textured_stencil_apply"))
.withDepthStencilState(new DepthStencilState(CompareOp.GREATER_THAN_OR_EQUAL, true, 0F, 0F, new StencilTest(
new StencilPerFaceTest(
StencilOperation.KEEP,
StencilOperation.KEEP,
StencilOperation.KEEP,
CompareOp.NOT_EQUAL),
0xFF,
0,
1)))
.build();
private static final Identifier MASK_TEXTURE = Identifier.withDefaultNamespace("textures/gui/sprites/icon/new_realm.png");
public StencilEnableTest(IEventBus modEventBus) {
if (ENABLED == State.DISABLE) {
@@ -70,137 +76,20 @@ public class StencilEnableTest {
modEventBus.addListener(ConfigureMainRenderTargetEvent.class, event -> {
event.enableStencil();
});
modEventBus.addListener(RegisterPipelineModifiersEvent.class, event -> {
event.register(STENCIL_FILL_KEY, (pipeline, name) -> pipeline.toBuilder()
.withLocation(name)
.withDepthStencilState(new DepthStencilState(CompareOp.GREATER_THAN_OR_EQUAL, true, 0F, 0F, new StencilTest(
new StencilPerFaceTest(
StencilOperation.KEEP,
StencilOperation.KEEP,
StencilOperation.REPLACE,
CompareOp.ALWAYS_PASS),
0xFF,
0xFF,
1)))
.build());
event.register(STENCIL_APPLY_KEY, (pipeline, name) -> pipeline.toBuilder()
.withLocation(name)
.withDepthStencilState(new DepthStencilState(CompareOp.GREATER_THAN_OR_EQUAL, true, 0F, 0F, new StencilTest(
new StencilPerFaceTest(
StencilOperation.KEEP,
StencilOperation.KEEP,
StencilOperation.KEEP,
CompareOp.NOT_EQUAL),
0xFF,
0,
1)))
.build());
});
modEventBus.addListener(RegisterPictureInPictureRenderersEvent.class, event -> {
if (ENABLED != State.ENABLE_UI_LAYER) {
return;
}
event.register(StenciledItemPictureInPictureRenderState.class, StenciledItemPictureInPictureRenderer::new);
});
modEventBus.addListener(RegisterGuiLayersEvent.class, event -> {
if (ENABLED != State.ENABLE_UI_LAYER) {
return;
}
event.registerAboveAll(
Identifier.fromNamespaceAndPath(MOD_ID, "block_outline"),
Identifier.fromNamespaceAndPath(MOD_ID, "stenciled_ui_element"),
(guiGraphics, _) -> {
ItemModelResolver itemModelResolver = Minecraft.getInstance().getItemModelResolver();
TrackingItemStackRenderState maskState = new TrackingItemStackRenderState();
itemModelResolver.updateForTopItem(maskState, new ItemStack(Blocks.GRASS_BLOCK), ItemDisplayContext.GUI, null, null, 0);
TrackingItemStackRenderState maskedState = new TrackingItemStackRenderState();
itemModelResolver.updateForTopItem(maskedState, new ItemStack(Blocks.DIAMOND_BLOCK), ItemDisplayContext.GUI, null, null, 0);
RenderSystem.getDevice().createCommandEncoder().clearStencilTexture(Minecraft.getInstance().gameRenderer.mainRenderTarget().getDepthTexture(), 0);
int maxX = guiGraphics.guiWidth();
guiGraphics.submitPictureInPictureRenderState(new StenciledItemPictureInPictureRenderState(
maskState,
maskedState,
maxX - 50, 10, maxX - 10, 50,
16F,
guiGraphics.peekScissorStack()));
guiGraphics.blit(GUI_TEXTURED_STENCIL_FILL, MASK_TEXTURE, maxX - 100, 10, 0, 0, 40, 20, 40, 20, 0x01FFFFFF);
guiGraphics.nextStratum();
guiGraphics.fill(GUI_STENCIL_APPLY, maxX - 100, 10, maxX - 60, 30, 0xFF0000AA);
});
});
}
// FIXME: it is no longer possible to render twice in a PiP
private static final class StenciledItemPictureInPictureRenderer extends PictureInPictureRenderer<StenciledItemPictureInPictureRenderState> {
@Override
protected void renderToTexture(StenciledItemPictureInPictureRenderState state, PoseStack poseStack, SubmitNodeCollector submitNodeCollector) {
Minecraft.getInstance().gameRenderer.lighting().setupFor(Lighting.Entry.ITEMS_3D);
poseStack.scale(1, -1, -1);
float scale = state.scale;
FeatureRenderDispatcher dispatcher = Minecraft.getInstance().gameRenderer.featureRenderDispatcher();
RenderSystem.pushPipelineModifier(STENCIL_FILL_KEY);
{
state.maskRenderState.submit(poseStack, submitNodeCollector, LightCoordsUtil.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, 0);
poseStack.pushPose();
poseStack.translate(10F / scale, -10F / scale, 0);
state.maskRenderState.submit(poseStack, submitNodeCollector, LightCoordsUtil.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, 0);
poseStack.popPose();
//dispatcher.renderAllFeatures((SubmitNodeStorage) submitNodeCollector);
}
RenderSystem.popPipelineModifier();
RenderSystem.pushPipelineModifier(STENCIL_APPLY_KEY);
{
poseStack.scale(1.1F, 1.1F, 1.1F);
poseStack.translate(-.5F / scale, .5F / scale, 0);
state.maskedRenderState.submit(poseStack, submitNodeCollector, LightCoordsUtil.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, 0);
poseStack.pushPose();
poseStack.translate(10F / scale, -10F / scale, 0);
state.maskedRenderState.submit(poseStack, submitNodeCollector, LightCoordsUtil.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, 0);
poseStack.popPose();
//dispatcher.renderAllFeatures((SubmitNodeStorage) submitNodeCollector);
}
RenderSystem.popPipelineModifier();
}
@Override
protected float getTranslateY(int height, int guiScale) {
return height / 2F;
}
@Override
public Class<StenciledItemPictureInPictureRenderState> getRenderStateClass() {
return StenciledItemPictureInPictureRenderState.class;
}
@Override
protected String getTextureLabel() {
return "stencil enable test";
}
}
private record StenciledItemPictureInPictureRenderState(
TrackingItemStackRenderState maskRenderState,
TrackingItemStackRenderState maskedRenderState,
int x0,
int y0,
int x1,
int y1,
float scale,
@Nullable ScreenRectangle bounds,
@Nullable ScreenRectangle scissorArea) implements PictureInPictureRenderState {
public StenciledItemPictureInPictureRenderState(
TrackingItemStackRenderState maskRenderState,
TrackingItemStackRenderState maskedRenderState,
int x0,
int y0,
int x1,
int y1,
float scale,
@Nullable ScreenRectangle scissorArea) {
this(maskRenderState, maskedRenderState, x0, y0, x1, y1, scale, PictureInPictureRenderState.getBounds(x0, y0, x1, y1, scissorArea), scissorArea);
}
}
}