diff --git a/module.gradle b/module.gradle index 29e4d03a..b247db82 100644 --- a/module.gradle +++ b/module.gradle @@ -53,6 +53,7 @@ neoForge { systemProperty('neoforge.enabledGameTestNamespaces', project.mod_id) systemProperty('neoforge.enabledGameTestNamespaces', project.mod_id) systemProperty("alrEnableDebugContext", "true") + // systemProperty("alrUseIntelBindlessImageArrayWorkaround", "2") jvmArgument("-XX:+UseParallelGC") jvmArgument("-XX:+UnlockDiagnosticVMOptions") jvmArgument("-XX:+DebugNonSafepoints") 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 index 9f811f83..2f286aef 100644 --- 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 @@ -11,13 +11,16 @@ public class ALROptions { public static final String OCCLUSION_CULLING_FORCE_IMPL = getProperty("alrOcclusionCullingForceImplementation", null); public static final boolean TEXTURE_DEBUG_CLEAR = getPropertyBoolean("alrTextureDebugClear", false); public static final boolean DEBUG_CONTEXT = getPropertyBoolean("alrEnableDebugContext", false); + // public static final int USE_INTEL_BINDLESS_IMAGE_ARRAY_WORKAROUND = getPropertyInt("alrUseIntelBindlessImageArrayWorkaround", 0); public static void logAllOptions() { log.info("ALR options: SPD_OPTION_WAVE_INTEROP_LDS={}, OCCLUSION_QUERY_USE_FRUSTUM_PRE_PASS={}, OCCLUSION_CULLING_FORCE_IMPL={}, TEXTURE_DEBUG_CLEAR={}", + // + ", USE_INTEL_BINDLESS_IMAGE_ARRAY_WORKAROUND={}", SPD_OPTION_WAVE_INTEROP_LDS, OCCLUSION_QUERY_USE_FRUSTUM_PRE_PASS, OCCLUSION_CULLING_FORCE_IMPL, TEXTURE_DEBUG_CLEAR + // , USE_INTEL_BINDLESS_IMAGE_ARRAY_WORKAROUND ); } @@ -33,6 +36,18 @@ private static boolean getPropertyBoolean(String key, boolean defaultValue) { return !"false".equals(prop); } + private static int getPropertyInt(String key, int defaultValue) { + String prop = System.getProperty(key); + if (prop == null) { + return defaultValue; + } + try { + return Integer.parseInt(prop); + } catch (NumberFormatException ignored) { + return defaultValue; + } + } + 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/extension/blaze3d/ALRHIHeuristics.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHIHeuristics.java index 9c8a8382..e0cdee5e 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHIHeuristics.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRHIHeuristics.java @@ -4,6 +4,6 @@ @ApiStatus.Internal public record ALRHIHeuristics( - boolean isWindowsArcGraphics + boolean isWindowsIntelGraphics ) { } 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 db32b3b7..b07d1dcb 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 @@ -47,6 +47,8 @@ public int getUniformLocation(String name, ALRGpuDeviceBackendExtension device) int orDefault = this.uniformLocationCache.getOrDefault(name, -2); if (orDefault == -2){ orDefault = device.alrGetUniformLocation(this, owner, name); + System.out.println("uniformName = " + name); + System.out.println("uniformLocation = " + orDefault); this.uniformLocationCache.put(name, orDefault); } return orDefault; diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/bindless/GlBindlessTexturingSupport.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/bindless/GlBindlessTexturingSupport.java index 02e049f2..32b38674 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/bindless/GlBindlessTexturingSupport.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/extension/blaze3d/texture/gl/bindless/GlBindlessTexturingSupport.java @@ -17,7 +17,10 @@ import org.jetbrains.annotations.ApiStatus; import org.lwjgl.opengl.ARBBindlessTexture; import org.lwjgl.opengl.GL46; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import java.nio.LongBuffer; import java.util.List; @ApiStatus.Internal @@ -126,6 +129,12 @@ public void alrBindTextureHandleMultiple( String name, List handle ) { + // Windows intel drivers has the wrong implementation of `glUniformHandleui64vARB` + if (this.backendExtension.alrhiCreateHeuristics().isWindowsIntelGraphics()) { + this.alrBindTextureHandleMultipleIntelBaseLocation(namedUniformAccess, name, handle); + return; + } + int uniformLocation = namedUniformAccess.getUniformLocation(name, this.backendExtension); long[] handles = new long[handle.size()]; @@ -137,6 +146,57 @@ public void alrBindTextureHandleMultiple( ARBBindlessTexture.glUniformHandleui64vARB(uniformLocation, handles); } + /// Queries each array element by name; requires element names to be accepted by NamedUniformAccess. + private void alrBindTextureHandleMultipleIntel( + NamedUniformAccess namedUniformAccess, + String name, + List handle + ) { + int i = 0; + for (TextureHandle textureHandle : handle) { + String uniformName = name + "[" + i++ + "]"; + int uniformLocation = namedUniformAccess.getUniformLocation(uniformName, this.backendExtension); + if (uniformLocation == -1) { + continue; + } + ARBBindlessTexture.glUniformHandleui64ARB(uniformLocation, handleId(textureHandle)); + } + } + + /// Query the location of array uniform as base location and set each element separately + private void alrBindTextureHandleMultipleIntelBaseLocation( + NamedUniformAccess namedUniformAccess, + String name, + List handle + ) { + int i = 0; + int locationStart = namedUniformAccess.getUniformLocation(name, this.backendExtension); + if (locationStart == -1) return; + for (TextureHandle textureHandle : handle) { + ARBBindlessTexture.glUniformHandleui64ARB(locationStart + i++, handleId(textureHandle)); + } + } + + /// Pads the input array + private void alrBindTextureHandleMultipleIntelPadded( + NamedUniformAccess namedUniformAccess, + String name, + List handle + ) { + int uniformLocation = namedUniformAccess.getUniformLocation(name, this.backendExtension); + + try (MemoryStack stack = MemoryStack.stackPush()) { + int count = handle.size(); + LongBuffer padded = stack.mallocLong(count * 2); + for (int i = 0; i < count; i++) { + padded.put(i * 2, handleId(handle.get(i))); + padded.put(i * 2 + 1, handleId(handle.get(i))); + } + + ARBBindlessTexture.nglUniformHandleui64vARB(uniformLocation, count, MemoryUtil.memAddress(padded)); + } + } + @SuppressWarnings("resource") @Override public void alrTextureDisposed(GpuTexture texture) { 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 6eb92631..a1482720 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 @@ -3,12 +3,10 @@ import com.mojang.blaze3d.GpuOutOfMemoryException; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlDebugLabel; +import com.mojang.blaze3d.opengl.GlDevice; import com.mojang.blaze3d.opengl.GlStateManager; -import com.mojang.blaze3d.opengl.GlTexture; -import com.mojang.blaze3d.platform.GLX; import com.mojang.blaze3d.preprocessor.GlslPreprocessor; import com.mojang.blaze3d.textures.GpuTexture; -import dev.anvilcraft.lib.v2.rendering.ALROptions; 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.ALRHIHeuristics; @@ -38,12 +36,11 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Locale; import java.util.function.Supplier; -@Mixin(targets = "com.mojang.blaze3d.opengl.GlDevice") +@Mixin(GlDevice.class) public abstract class GlDeviceMixin implements ALRGpuDeviceBackendExtension { @Shadow @@ -145,32 +142,25 @@ public ALRHICapabilities alrhiCreateCapabilities() { @Override public ALRHIHeuristics alrhiCreateHeuristics() { if (this.alr$heuristics == null) { - this.alr$heuristics = new ALRHIHeuristics(this.alr$isWindowsArcGraphics()); + this.alr$heuristics = new ALRHIHeuristics(this.alr$isWindowsIntelGraphics()); } return this.alr$heuristics; } @Unique - private boolean alr$isWindowsArcGraphics() { + private boolean alr$isWindowsIntelGraphics() { if (Util.getPlatform() != Util.OS.WINDOWS) { return false; } - String renderer = this.getRenderer(); - String lowerRenderer = renderer.toLowerCase(Locale.ROOT); - if (!lowerRenderer.contains("intel")) { - return false; - } - String cpuInfo = GLX._getCpuInfo(); - String lowerCpuInfo = cpuInfo.toLowerCase(Locale.ROOT); - boolean windowsArcGraphics = lowerRenderer.contains("arc") - || (lowerCpuInfo.contains("intel") && lowerCpuInfo.contains("ultra")); + String vendor = GL11.glGetString(GL11.GL_VENDOR); + boolean windowsIntelGraphics = vendor != null && vendor.toLowerCase(Locale.ROOT).contains("intel"); LOGGER.info( - "Windows graphics info: renderer='{}', cpu='{}', windowsArcGraphics={}", - renderer, - cpuInfo, - windowsArcGraphics + "Windows graphics info: renderer='{}', vendor='{}', windowsIntelGraphics={}", + this.getRenderer(), + vendor, + windowsIntelGraphics ); - return windowsArcGraphics; + return windowsIntelGraphics; } @Override diff --git a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZSupport.java b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZSupport.java index d27cffc8..d5195567 100644 --- a/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZSupport.java +++ b/module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/optimization/occlusion/hiz/HierarchicalZSupport.java @@ -1,11 +1,12 @@ package dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz; import dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension; +import dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz.spd.SinglePassDownsampler; import org.jetbrains.annotations.ApiStatus; /// Device policy for the hierarchical-Z backend. /// -/// The {@link dev.anvilcraft.lib.v2.rendering.optimization.occlusion.hiz.spd.SinglePassDownsampler SPD pass} +/// The {@link SinglePassDownsampler SPD pass} /// and the Hi-Z occlusion test bind their mip chain either as consecutive image /// units, or - on devices that cannot spare that many units - through a bindless /// image array: @@ -15,9 +16,8 @@ /// `layout(binding = N)` index that is not below `GL_MAX_IMAGE_UNITS`. /// {@link #REQUIRED_IMAGE_UNITS} is the smallest power of two that satisfies /// this requirement. -/// - The bindless fallback is what makes hierarchical-Z usable on devices with -/// fewer image units, but it is broken on Intel Arc graphics under Windows: -/// the driver advertises `GL_ARB_bindless_texture`, yet using it crashes the JVM. +/// - The bindless fallback makes hierarchical-Z usable on devices with fewer +/// image units. Windows Intel image arrays use the per-element upload workaround. @ApiStatus.Internal public final class HierarchicalZSupport { /// `GL_MAX_IMAGE_UNITS` needed to bind the SPD mip chain as image units. @@ -46,8 +46,7 @@ public static boolean hasEnoughImageUnits(ALRGpuDeviceExtension device) { /// @return `true` when the bindless path has to be and may be used public static boolean useBindlessTexturing(ALRGpuDeviceExtension device) { return !hasEnoughImageUnits(device) - && device.alrhiCreateCapabilities().bindlessTexturing() - && !device.alrhiCreateHeuristics().isWindowsArcGraphics(); + && device.alrhiCreateCapabilities().bindlessTexturing(); } /// Returns whether hierarchical-Z can run on the device at all.