/* * This file is part of the LoVisual Client distribution. * Copyright (c) 2026 pivosos2007. * * Licensed under the GNU General Public License v3.0. */ package dev.loki.lovisual.mixins; import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.systems.RenderPassBackend; import com.mojang.blaze3d.systems.RenderPassDescriptor; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.vulkan.VulkanCommandEncoder; import com.mojang.blaze3d.vulkan.VulkanGpuTextureView; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.VkClearDepthStencilValue; import org.lwjgl.vulkan.VkClearValue; import org.lwjgl.vulkan.VkRenderingAttachmentInfo; import org.lwjgl.vulkan.VkRenderingInfo; 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; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import dev.loki.lovisual.render.engine.core.LoVisualRenderSystem; import dev.loki.lovisual.render.engine.rhi.backend.vulkan.clip.VulkanShapeClipBridge; import dev.loki.lovisual.render.engine.rhi.backend.vulkan.msaa.VulkanMsaaResolveBridge; import dev.loki.lovisual.render.engine.rhi.backend.vulkan.util.VulkanRenderStateBridge; import static org.lwjgl.vulkan.VK12.*; @Mixin(VulkanCommandEncoder.class) public abstract class VulkanCommandEncoderMixin { @Inject(method = "createRenderPass", at = @At("HEAD")) private void lovisual$beginVulkanRenderPassState(RenderPassDescriptor descriptor, CallbackInfoReturnable cir) { VulkanMsaaResolveBridge.beginRenderPass(); VulkanRenderStateBridge.beginRenderPass(descriptor); } @Inject( method = "createRenderPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/vulkan/KHRDynamicRendering;vkCmdBeginRenderingKHR(Lorg/lwjgl/vulkan/VkCommandBuffer;Lorg/lwjgl/vulkan/VkRenderingInfo;)V", shift = At.Shift.BEFORE) ) private void lovisual$configureAttachments(RenderPassDescriptor descriptor, CallbackInfoReturnable cir, @Local MemoryStack stack, @Local VkRenderingInfo renderingInfo) { if (VulkanRenderStateBridge.currentRenderPassNeedsStencilAttachment()) { GpuTextureView view = VulkanShapeClipBridge.stencilAttachmentFor(descriptor); if (!(view instanceof VulkanGpuTextureView vkView) || view.isClosed()) { VulkanRenderStateBridge.markCurrentRenderPassStencilAttachment(false); } else { VulkanRenderStateBridge.markCurrentRenderPassStencilAttachment(true); boolean clear = VulkanShapeClipBridge.consumeStencilClear(); VkRenderingAttachmentInfo stencilAttachment = VkRenderingAttachmentInfo.calloc(stack).sType$Default(); stencilAttachment .imageView(vkView.vkImageView()) .imageLayout(VK_IMAGE_LAYOUT_GENERAL) .storeOp(VK_ATTACHMENT_STORE_OP_STORE) .loadOp(clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD); if (clear) { VkClearDepthStencilValue clearValue = VkClearDepthStencilValue.calloc(stack).depth(0.0f).stencil(0); stencilAttachment.clearValue(VkClearValue.calloc(stack).depthStencil(clearValue)); } renderingInfo.pStencilAttachment(stencilAttachment); } } try { VulkanMsaaResolveBridge.configure(descriptor, renderingInfo); } catch (Throwable t) { boolean snapshotResolve = VulkanMsaaResolveBridge.snapshotActive(); VulkanMsaaResolveBridge.abortRenderPass(); if (!snapshotResolve) { VulkanRenderStateBridge.disableMsaaAfterFailure("dynamic rendering resolve configuration", t); } throw t instanceof RuntimeException runtime ? runtime : new IllegalStateException("Failed to configure Vulkan MSAA resolve", t); } } @Inject(method = "submitRenderPass", at = @At("TAIL")) private void lovisual$endVulkanRenderPassState(CallbackInfo ci) { VulkanMsaaResolveBridge.finishRenderPass(); try { LoVisualRenderSystem.rhi().shapeClip().endRenderPass(); } catch (Throwable ignored) { // Render pass teardown must never make Vulkan submission fail. } VulkanRenderStateBridge.endRenderPass(); } }