From 46ef39691dbac8f46f118de7f5041aec99c57d97 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 21 Apr 2026 11:19:43 +0200 Subject: [PATCH 1/3] UGpuTexture: Add This commit introduce a new `UGpuTexture` API to UniversalCraft. This API mimics Mojang's Blaze3D API, but works on all versions supported by UniversalCraft. Initially, only the most fundamental functionality is provided, together with a way to convert from/to the underlynig Minecraft type / OpenGL id. This is enough for us to start supporting Vulkan without having to lock in the API of all the more complex operations like copying between textures, updating only parts of a texture, and reading the pixel data back from gpu memory. Most of these will likely be migrated from Essential into this new API in the future as well, just not yet with this initial commit. This commit also adds a new `UGpuSampler` type, which allows one to configure texture filtering when using UC's `DrawCallBuilder`. This isn't directly related to the `UGpuTexture` API, but both changes modify the arguments of the same `DrawCallBuilder.texture` method, so I've combined. --- api/UniversalCraft.api | 106 ++++++++++ .../gg/essential/universal/UGraphics.java | 12 ++ .../universal/render/DrawCallBuilder.kt | 5 + .../essential/universal/render/UGpuDevice.kt | 22 ++ .../universal/render/UGpuDeviceImpl.kt | 191 ++++++++++++++++++ .../universal/render/UGpuPlatformAdapter.kt | 42 ++++ .../render/UGpuPlatformAdapterImpl.kt | 83 ++++++++ .../essential/universal/render/UGpuSampler.kt | 53 +++++ .../universal/render/UGpuSamplerImpl.kt | 50 +++++ .../essential/universal/render/UGpuTexture.kt | 48 +++++ .../universal/render/UGpuTextureImpl.kt | 160 +++++++++++++++ .../essential/universal/render/URenderPass.kt | 29 +++ .../universal/utils/ReleasedDynamicTexture.kt | 124 +++++------- versions/1.17.1-1.16.2.txt | 1 + 14 files changed, 856 insertions(+), 70 deletions(-) create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuDevice.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapter.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuSampler.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuSamplerImpl.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt create mode 100644 src/main/kotlin/gg/essential/universal/render/UGpuTextureImpl.kt diff --git a/api/UniversalCraft.api b/api/UniversalCraft.api index dbb80483..58adabdd 100644 --- a/api/UniversalCraft.api +++ b/api/UniversalCraft.api @@ -192,9 +192,11 @@ public class gg/essential/universal/UGraphics { public fun endVertex ()Lgg/essential/universal/UGraphics; public static fun getActiveTexture ()I public static fun getCharWidth (C)F + public static fun getDevice ()Lgg/essential/universal/render/UGpuDevice; public static fun getEmptyTexture ()Lgg/essential/universal/utils/ReleasedDynamicTexture; public static fun getFontHeight ()I public static fun getFromTessellator ()Lgg/essential/universal/UGraphics; + public static fun getPlatformAdapter ()Lgg/essential/universal/render/UGpuPlatformAdapter; public static fun getStringWidth (Ljava/lang/String;)I @1.17.1-forge,1.18.1-forge,1.19.2-forge,1.19.3-forge,1.19.4-forge,1.20.1-forge,1.20.2-forge,1.20.4-forge,1.20.4-neoforge,1.20.6-forge,1.20.6-neoforge,1.21-forge,1.21-neoforge,1.21.3-forge,1.21.3-neoforge,1.21.4-forge,1.21.4-neoforge,1.21.5-forge,1.21.5-neoforge,1.21.7-forge,1.21.7-neoforge,26.1-fabric public static fun getTessellator ()Lcom/mojang/blaze3d/vertex/Tesselator; @@ -1106,11 +1108,109 @@ public abstract interface class gg/essential/universal/render/DrawCallBuilder { public abstract fun noScissor ()Lgg/essential/universal/render/DrawCallBuilder; public abstract fun scissor (IIII)Lgg/essential/universal/render/DrawCallBuilder; public abstract fun texture (II)Lgg/essential/universal/render/DrawCallBuilder; + public abstract fun texture (ILgg/essential/universal/render/UGpuTextureView;Lgg/essential/universal/render/UGpuSampler;)Lgg/essential/universal/render/DrawCallBuilder; public abstract fun texture (Ljava/lang/String;I)Lgg/essential/universal/render/DrawCallBuilder; + public abstract fun texture (Ljava/lang/String;Lgg/essential/universal/render/UGpuTextureView;Lgg/essential/universal/render/UGpuSampler;)Lgg/essential/universal/render/DrawCallBuilder; public abstract fun uniform (Ljava/lang/String;[F)Lgg/essential/universal/render/DrawCallBuilder; public abstract fun uniform (Ljava/lang/String;[I)Lgg/essential/universal/render/DrawCallBuilder; } +public abstract interface class gg/essential/universal/render/UGpuDevice { + public abstract fun createTexture (Ljava/lang/String;Lgg/essential/universal/render/UGpuTexture$Usage;Lgg/essential/universal/render/UGpuFormat;III)Lgg/essential/universal/render/UGpuTexture; + public static synthetic fun createTexture$default (Lgg/essential/universal/render/UGpuDevice;Ljava/lang/String;Lgg/essential/universal/render/UGpuTexture$Usage;Lgg/essential/universal/render/UGpuFormat;IIIILjava/lang/Object;)Lgg/essential/universal/render/UGpuTexture; + public abstract fun createTextureView (Lgg/essential/universal/render/UGpuTexture;II)Lgg/essential/universal/render/UGpuTextureView; + public static synthetic fun createTextureView$default (Lgg/essential/universal/render/UGpuDevice;Lgg/essential/universal/render/UGpuTexture;IIILjava/lang/Object;)Lgg/essential/universal/render/UGpuTextureView; +} + +public abstract interface class gg/essential/universal/render/UGpuFormat { + public static final field Companion Lgg/essential/universal/render/UGpuFormat$Companion; +} + +public final class gg/essential/universal/render/UGpuFormat$Companion { + public final fun getDEFAULT_DEPTH ()Lgg/essential/universal/render/UGpuFormat; + public final fun getDEFAULT_RGBA ()Lgg/essential/universal/render/UGpuFormat; +} + +public abstract interface class gg/essential/universal/render/UGpuPlatformAdapter { + public abstract fun getDefaultGpuFormatDepth ()Lgg/essential/universal/render/UGpuFormat; + public abstract fun getDefaultGpuFormatRgba ()Lgg/essential/universal/render/UGpuFormat; + @26.2-fabric + public abstract fun gpuFormat (Lcom/mojang/blaze3d/GpuFormat;)Lgg/essential/universal/render/UGpuFormat; + @1.12.2-forge,1.16.2-fabric,1.16.2-forge,1.17.1-fabric,1.17.1-forge,1.18.1-fabric,1.18.1-forge,1.19-fabric,1.19.1-fabric,1.19.2-fabric,1.19.2-forge,1.19.3-fabric,1.19.3-forge,1.19.4-fabric,1.19.4-forge,1.20-fabric,1.20.1-fabric,1.20.1-forge,1.20.2-fabric,1.20.2-forge,1.20.4-fabric,1.20.4-forge,1.20.4-neoforge,1.20.6-fabric,1.20.6-forge,1.20.6-neoforge,1.21-fabric,1.21-forge,1.21-neoforge,1.21.11-fabric,1.21.3-fabric,1.21.3-forge,1.21.3-neoforge,1.21.4-fabric,1.21.4-forge,1.21.4-neoforge,1.21.5-fabric,1.21.5-forge,1.21.5-neoforge,1.21.6-fabric,1.21.7-fabric,1.21.7-forge,1.21.7-neoforge,1.21.9-fabric,1.8.9-forge,26.1-fabric + public abstract fun gpuFormat (III)Lgg/essential/universal/render/UGpuFormat; + @1.21.11-fabric,1.21.5-fabric,1.21.5-forge,1.21.5-neoforge,1.21.6-fabric,1.21.7-fabric,1.21.7-forge,1.21.7-neoforge,1.21.9-fabric,26.1-fabric,26.2-fabric + public abstract fun texture (Lcom/mojang/blaze3d/textures/GpuTexture;)Lgg/essential/universal/render/UGpuTexture; + @1.21.11-fabric,1.21.5-fabric,1.21.5-forge,1.21.5-neoforge,1.21.6-fabric,1.21.7-fabric,1.21.7-forge,1.21.7-neoforge,1.21.9-fabric,26.1-fabric,26.2-fabric + public abstract fun texture (Lgg/essential/universal/render/UGpuTexture;)Lcom/mojang/blaze3d/textures/GpuTexture; + @1.21.11-fabric,1.21.6-fabric,1.21.7-fabric,1.21.7-forge,1.21.7-neoforge,1.21.9-fabric,26.1-fabric,26.2-fabric + public abstract fun textureView (Lcom/mojang/blaze3d/textures/GpuTextureView;)Lgg/essential/universal/render/UGpuTextureView; + @1.21.11-fabric,1.21.6-fabric,1.21.7-fabric,1.21.7-forge,1.21.7-neoforge,1.21.9-fabric,26.1-fabric,26.2-fabric + public abstract fun textureView (Lgg/essential/universal/render/UGpuTextureView;)Lcom/mojang/blaze3d/textures/GpuTextureView; + @1.12.2-forge,1.16.2-fabric,1.16.2-forge,1.17.1-fabric,1.17.1-forge,1.18.1-fabric,1.18.1-forge,1.19-fabric,1.19.1-fabric,1.19.2-fabric,1.19.2-forge,1.19.3-fabric,1.19.3-forge,1.19.4-fabric,1.19.4-forge,1.20-fabric,1.20.1-fabric,1.20.1-forge,1.20.2-fabric,1.20.2-forge,1.20.4-fabric,1.20.4-forge,1.20.4-neoforge,1.20.6-fabric,1.20.6-forge,1.20.6-neoforge,1.21-fabric,1.21-forge,1.21-neoforge,1.21.3-fabric,1.21.3-forge,1.21.3-neoforge,1.21.4-fabric,1.21.4-forge,1.21.4-neoforge,1.8.9-forge + public abstract fun texture (ILgg/essential/universal/render/UGpuFormat;III)Lgg/essential/universal/render/UGpuTexture; + @1.12.2-forge,1.16.2-fabric,1.16.2-forge,1.17.1-fabric,1.17.1-forge,1.18.1-fabric,1.18.1-forge,1.19-fabric,1.19.1-fabric,1.19.2-fabric,1.19.2-forge,1.19.3-fabric,1.19.3-forge,1.19.4-fabric,1.19.4-forge,1.20-fabric,1.20.1-fabric,1.20.1-forge,1.20.2-fabric,1.20.2-forge,1.20.4-fabric,1.20.4-forge,1.20.4-neoforge,1.20.6-fabric,1.20.6-forge,1.20.6-neoforge,1.21-fabric,1.21-forge,1.21-neoforge,1.21.3-fabric,1.21.3-forge,1.21.3-neoforge,1.21.4-fabric,1.21.4-forge,1.21.4-neoforge,1.8.9-forge + public abstract fun texture (Lgg/essential/universal/render/UGpuTexture;)I +} + +public abstract interface class gg/essential/universal/render/UGpuSampler { + public static final field Companion Lgg/essential/universal/render/UGpuSampler$Companion; +} + +public final class gg/essential/universal/render/UGpuSampler$AddressMode : java/lang/Enum { + public static final field CLAMP_TO_EDGE Lgg/essential/universal/render/UGpuSampler$AddressMode; + public static final field REPEAT Lgg/essential/universal/render/UGpuSampler$AddressMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lgg/essential/universal/render/UGpuSampler$AddressMode; + public static fun values ()[Lgg/essential/universal/render/UGpuSampler$AddressMode; +} + +public final class gg/essential/universal/render/UGpuSampler$Companion { + public final fun get (Lgg/essential/universal/render/UGpuSampler$AddressMode;Lgg/essential/universal/render/UGpuSampler$AddressMode;Lgg/essential/universal/render/UGpuSampler$FilterMode;Lgg/essential/universal/render/UGpuSampler$FilterMode;Z)Lgg/essential/universal/render/UGpuSampler; +} + +public final class gg/essential/universal/render/UGpuSampler$FilterMode : java/lang/Enum { + public static final field LINEAR Lgg/essential/universal/render/UGpuSampler$FilterMode; + public static final field NEAREST Lgg/essential/universal/render/UGpuSampler$FilterMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lgg/essential/universal/render/UGpuSampler$FilterMode; + public static fun values ()[Lgg/essential/universal/render/UGpuSampler$FilterMode; +} + +public abstract interface class gg/essential/universal/render/UGpuTexture : java/io/Closeable { + public abstract fun getHeight ()I + public abstract fun getMipLevels ()I + public abstract fun getWidth ()I + public abstract fun isClosed ()Z +} + +public final class gg/essential/universal/render/UGpuTexture$Usage { + public static final field Companion Lgg/essential/universal/render/UGpuTexture$Usage$Companion; + public fun (I)V + public final fun component1 ()I + public final fun contains (Lgg/essential/universal/render/UGpuTexture$Usage;)Z + public final fun copy (I)Lgg/essential/universal/render/UGpuTexture$Usage; + public static synthetic fun copy$default (Lgg/essential/universal/render/UGpuTexture$Usage;IILjava/lang/Object;)Lgg/essential/universal/render/UGpuTexture$Usage; + public fun equals (Ljava/lang/Object;)Z + public final fun getBits ()I + public fun hashCode ()I + public final fun plus (Lgg/essential/universal/render/UGpuTexture$Usage;)Lgg/essential/universal/render/UGpuTexture$Usage; + public fun toString ()Ljava/lang/String; +} + +public final class gg/essential/universal/render/UGpuTexture$Usage$Companion { + public final fun getCOPY_DST ()Lgg/essential/universal/render/UGpuTexture$Usage; + public final fun getCOPY_SRC ()Lgg/essential/universal/render/UGpuTexture$Usage; + public final fun getRENDER_ATTACHMENT ()Lgg/essential/universal/render/UGpuTexture$Usage; + public final fun getTEXTURE_BINDING ()Lgg/essential/universal/render/UGpuTexture$Usage; +} + +public abstract interface class gg/essential/universal/render/UGpuTextureView : java/io/Closeable { + public abstract fun getBaseMipLevel ()I + public abstract fun getMipLevels ()I + public abstract fun getTexture ()Lgg/essential/universal/render/UGpuTexture; + public abstract fun isClosed ()Z +} + public final class gg/essential/universal/render/URenderPipeline { public static final field Companion Lgg/essential/universal/render/URenderPipeline$Companion; @26.1-fabric,26.2-fabric @@ -1360,6 +1460,8 @@ public final class gg/essential/universal/utils/ReleasedDynamicTexture : net/min public fun deleteGlTexture ()V public final fun getDynamicGlId ()I public fun getGlTextureId ()I + public final fun getGpuTexture ()Lgg/essential/universal/render/UGpuTexture; + public final fun getGpuTextureView ()Lgg/essential/universal/render/UGpuTextureView; public final fun getHeight ()I public final fun getUploaded ()Z public final fun getWidth ()I @@ -1383,6 +1485,8 @@ public final class gg/essential/universal/utils/ReleasedDynamicTexture : net/min public fun getGlTextureView ()Lcom/mojang/blaze3d/textures/GpuTextureView; @1.16.2-fabric,1.17.1-fabric,1.18.1-fabric,1.19-fabric,1.19.1-fabric,1.19.2-fabric,1.19.3-fabric,1.19.4-fabric,1.20-fabric,1.20.1-fabric,1.20.2-fabric,1.20.4-fabric,1.20.6-fabric,1.21-fabric,1.21.3-fabric,1.21.4-fabric public fun getGlId ()I + public final fun getGpuTexture ()Lgg/essential/universal/render/UGpuTexture; + public final fun getGpuTextureView ()Lgg/essential/universal/render/UGpuTextureView; public final fun getHeight ()I public final fun getUploaded ()Z public final fun getWidth ()I @@ -1424,6 +1528,8 @@ public final class gg/essential/universal/utils/ReleasedDynamicTexture : net/min public final fun getDynamicGlId ()I @1.12.2-forge,1.8.9-forge public fun getGlTextureId ()I + public final fun getGpuTexture ()Lgg/essential/universal/render/UGpuTexture; + public final fun getGpuTextureView ()Lgg/essential/universal/render/UGpuTextureView; public final fun getHeight ()I @1.21.5-forge,1.21.5-neoforge,1.21.7-forge,1.21.7-neoforge,26.1-fabric,26.2-fabric public fun getTexture ()Lcom/mojang/blaze3d/textures/GpuTexture; diff --git a/src/main/java/gg/essential/universal/UGraphics.java b/src/main/java/gg/essential/universal/UGraphics.java index ff9ea4d5..ce696c8a 100644 --- a/src/main/java/gg/essential/universal/UGraphics.java +++ b/src/main/java/gg/essential/universal/UGraphics.java @@ -1,6 +1,10 @@ package gg.essential.universal; import gg.essential.universal.render.ScissorState; +import gg.essential.universal.render.UGpuDevice; +import gg.essential.universal.render.UGpuDeviceImpl; +import gg.essential.universal.render.UGpuPlatformAdapter; +import gg.essential.universal.render.UGpuPlatformAdapterImpl; import gg.essential.universal.shader.BlendState; import gg.essential.universal.utils.ReleasedDynamicTexture; import gg.essential.universal.vertex.UVertexConsumer; @@ -221,6 +225,14 @@ public static UGraphics getFromTessellator() { //#endif } + public static UGpuPlatformAdapter getPlatformAdapter() { + return UGpuPlatformAdapterImpl.INSTANCE; + } + + public static UGpuDevice getDevice() { + return UGpuDeviceImpl.INSTANCE; + } + //#if MC<11700 @Deprecated // See UGraphics.GL public static void pushMatrix() { diff --git a/src/main/kotlin/gg/essential/universal/render/DrawCallBuilder.kt b/src/main/kotlin/gg/essential/universal/render/DrawCallBuilder.kt index a464f4fe..e77c61c8 100644 --- a/src/main/kotlin/gg/essential/universal/render/DrawCallBuilder.kt +++ b/src/main/kotlin/gg/essential/universal/render/DrawCallBuilder.kt @@ -10,6 +10,11 @@ interface DrawCallBuilder { fun uniform(name: String, vararg values: Int): DrawCallBuilder fun uniform(name: String, vararg values: Float): DrawCallBuilder + fun texture(name: String, textureView: UGpuTextureView, sampler: UGpuSampler): DrawCallBuilder + fun texture(index: Int, textureView: UGpuTextureView, sampler: UGpuSampler): DrawCallBuilder + + @Deprecated("Does not support Vulkan; uses hard-coded sampler on 1.21.11+, relies on texture configuration on older versions.") fun texture(name: String, textureGlId: Int): DrawCallBuilder + @Deprecated("Does not support Vulkan; uses hard-coded sampler on 1.21.11+, relies on texture configuration on older versions.") fun texture(index: Int, textureGlId: Int): DrawCallBuilder } diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuDevice.kt b/src/main/kotlin/gg/essential/universal/render/UGpuDevice.kt new file mode 100644 index 00000000..18f37d96 --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuDevice.kt @@ -0,0 +1,22 @@ +package gg.essential.universal.render + +import org.jetbrains.annotations.ApiStatus.NonExtendable +import java.nio.ByteBuffer + +@NonExtendable +sealed interface UGpuDevice { + fun createTexture( + label: String?, + usage: UGpuTexture.Usage, + format: UGpuFormat, + width: Int, + height: Int, + mipLevels: Int = 1, + ): UGpuTexture + + fun createTextureView( + texture: UGpuTexture, + baseMipLevel: Int = 0, + mipLevels: Int = texture.mipLevels, + ): UGpuTextureView +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt new file mode 100644 index 00000000..396ead4f --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt @@ -0,0 +1,191 @@ +package gg.essential.universal.render + +import gg.essential.universal.UGraphics +import java.nio.ByteBuffer +import kotlin.math.max +import org.lwjgl.opengl.GL11 + +//#if STANDALONE +//$$ import org.lwjgl.opengl.GL20C +//#else +//#if MC >= 1.21.5 +//$$ import com.mojang.blaze3d.textures.TextureFormat +//$$ import net.minecraft.client.texture.GlTexture +//#endif + +//#if MC >= 1.21.5 && MC < 26.1 +//$$ import net.minecraft.client.gl.GlBackend +//#endif + +//#if MC >= 1.16 +//$$ import com.mojang.blaze3d.platform.GlStateManager +//#endif + +import net.minecraft.client.renderer.GlStateManager +//#endif + +internal object UGpuDeviceImpl : UGpuDevice { + override fun createTexture( + label: String?, + usage: UGpuTexture.Usage, + format: UGpuFormat, + width: Int, + height: Int, + mipLevels: Int + ): UGpuTexture { + require(usage.bits != 0) { "At least one usage bit must be set" } + require(width > 0) { "Width must be positive but was $width" } + require(height > 0) { "Height must be positive but was $height" } + require(mipLevels > 0) { "Mip levels must be positive but was $mipLevels"} + fun log2(x: Int) = 31 - x.countLeadingZeroBits() + val maxMipLevels = log2(max(width, height)) + 1 + require(mipLevels <= maxMipLevels) { "Texture of size ${width}x${height} supports at most $maxMipLevels but $mipLevels were requested" } + + //#if STANDALONE + //$$ return createGlTexture(label, usage, format.impl, width, height, mipLevels) + //#elseif MC >= 26.2 + //$$ return createB3DTexture(label, usage, format.impl.mc, width, height, mipLevels) + //#elseif MC >= 1.21.5 + //$$ return when (format) { + //$$ UGpuPlatformAdapterImpl.defaultGpuFormatRgba -> + //$$ createB3DTexture(label, usage, format.impl, TextureFormat.RGBA8, width, height, mipLevels) + //$$ UGpuPlatformAdapterImpl.defaultGpuFormatDepth -> + //$$ createB3DTexture(label, usage, format.impl, TextureFormat.DEPTH32, width, height, mipLevels) + //$$ else -> + //$$ createGlTexture(label, usage, format.impl, width, height, mipLevels) + //$$ } + //#else + return createGlTexture(label, usage, format.impl, width, height, mipLevels) + //#endif + } + + //#if MC >= 1.21.5 && !STANDALONE + //$$ private fun createB3DTexture( + //$$ label: String?, + //$$ usage: UGpuTexture.Usage, + //#if MC < 26.2 + //$$ format: UGpuFormatImpl, + //#endif + //$$ b3dFormat: TextureFormat, + //$$ width: Int, + //$$ height: Int, + //$$ mipLevels: Int + //$$ ): UGpuTextureImpl { + //$$ val b3dTexture = RenderSystem.getDevice().createTexture( + //$$ label, + //#if MC >= 1.21.6 + //$$ usage.bits, + //#endif + //$$ b3dFormat, + //$$ width, + //$$ height, + //#if MC >= 1.21.6 + //$$ 1, + //#endif + //$$ mipLevels, + //$$ ) + //$$ return UGpuTextureImpl( + //#if MC < 1.21.6 + //$$ usage, + //#endif + //#if MC < 26.2 + //$$ format, + //#endif + //$$ b3dTexture, + //$$ ) + //$$ } + //#endif + + //#if MC < 26.2 || STANDALONE + private fun createGlTexture( + label: String?, + usage: UGpuTexture.Usage, + format: UGpuFormatImpl, + width: Int, + height: Int, + mipLevels: Int + ): UGpuTextureImpl { + //#if STANDALONE + //$$ val glId = GL20C.glGenTextures() + //#elseif MC >= 1.16 + //$$ val glId = GlStateManager.genTexture() + //#else + val glId = GlStateManager.generateTexture() + //#endif + + UGraphics.configureTexture(glId) { + GL11.glTexImage2D( + GL11.GL_TEXTURE_2D, + 0, + format.internalFormat, + width, + height, + 0, + format.format, + format.type, + null as ByteBuffer?, + ) + } + + //#if MC >= 1.21.5 && !STANDALONE + //$$ val glTexture = object : GlTexture( + //#if MC >= 1.21.6 + //$$ usage.bits, + //#endif + //$$ label ?: "$glId", + //$$ if (format.hasDepth) TextureFormat.DEPTH32 else TextureFormat.RGBA8, + //$$ width, + //$$ height, + //#if MC >= 1.21.6 + //$$ 1, // depthOrLayers + //#endif + //$$ mipLevels, + //$$ glId, + //$$ ) {} + //#endif + + //#if MC >= 1.21.5 && MC < 26.1 + //$$ (RenderSystem.getDevice() as? GlBackend)?.debugLabelManager?.labelGlTexture(glTexture) + //#else + // TODO could backport this + @Suppress("unused", "UNUSED_VARIABLE") + val _label = label + //#endif + + return UGpuTextureImpl( + //#if MC < 1.21.6 || STANDALONE + usage, + //#endif + //#if MC < 26.2 || STANDALONE + format, + //#endif + //#if MC >= 1.21.5 && !STANDALONE + //$$ glTexture, + //#else + glId, + width, + height, + mipLevels, + //#endif + ) + } + //#endif + + override fun createTextureView( + texture: UGpuTexture, + baseMipLevel: Int, + mipLevels: Int + ): UGpuTextureView { + require(!texture.isClosed) { "Texture is closed" } + require(baseMipLevel >= 0) { "Base mip level cannot be negative but was $baseMipLevel" } + require(mipLevels > 0) { "Mip levels must be positive but was $mipLevels" } + require(baseMipLevel + mipLevels <= texture.mipLevels) { "$baseMipLevel + $mipLevels exceeds available mip levels (${texture.mipLevels})" } + + //#if MC >= 1.21.6 && !STANDALONE + //$$ val view = RenderSystem.getDevice().createTextureView(texture.impl.mc) + //$$ return UGpuTextureViewImpl(texture.impl, view) + //#else + return UGpuTextureViewImpl(texture.impl, baseMipLevel, mipLevels) + //#endif + } +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapter.kt b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapter.kt new file mode 100644 index 00000000..1f8928fb --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapter.kt @@ -0,0 +1,42 @@ +package gg.essential.universal.render + +import org.jetbrains.annotations.ApiStatus.NonExtendable + +//#if STANDALONE +//#else +//#if MC >= 26.2 +//$$ import com.mojang.blaze3d.GpuFormat +//#endif +//#if MC >= 1.21.6 +//$$ import com.mojang.blaze3d.textures.GpuTextureView +//#endif +//#if MC >= 1.21.5 +//$$ import com.mojang.blaze3d.textures.GpuTexture +//#endif +//#endif + +@NonExtendable +sealed interface UGpuPlatformAdapter { + val defaultGpuFormatRgba: UGpuFormat + val defaultGpuFormatDepth: UGpuFormat + + //#if MC >= 26.2 && !STANDALONE + //$$ fun gpuFormat(gpuFormat: GpuFormat): UGpuFormat + //#else + /** @see org.lwjgl.opengl.GL11.glTexImage2D */ + fun gpuFormat(internalFormat: Int, format: Int, type: Int): UGpuFormat + //#endif + + //#if MC >= 1.21.5 && !STANDALONE + //$$ fun texture(texture: GpuTexture): UGpuTexture + //$$ fun texture(texture: UGpuTexture): GpuTexture + //#else + fun texture(glId: Int, format: UGpuFormat, width: Int, height: Int, mipLevels: Int): UGpuTexture + fun texture(texture: UGpuTexture): Int + //#endif + + //#if MC >= 1.21.6 && !STANDALONE + //$$ fun textureView(textureView: GpuTextureView): UGpuTextureView + //$$ fun textureView(textureView: UGpuTextureView): GpuTextureView + //#endif +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt new file mode 100644 index 00000000..b8df9f62 --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt @@ -0,0 +1,83 @@ +package gg.essential.universal.render + +import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL14 + +//#if STANDALONE +//#else +//#if MC >= 1.21.6 +//$$ import com.mojang.blaze3d.textures.GpuTextureView +//#endif +//#if MC >= 1.21.5 +//$$ import com.mojang.blaze3d.textures.GpuTexture +//$$ import com.mojang.blaze3d.textures.TextureFormat +//$$ import org.lwjgl.opengl.GL30 +//#endif +//#endif + +internal object UGpuPlatformAdapterImpl : UGpuPlatformAdapter { + override val defaultGpuFormatRgba: UGpuFormatImpl = + //#if MC >= 26.2 && !STANDALONE + //$$ gpuFormat(GpuFormat.RGBA8_UNORM) + //#else + gpuFormat(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE) + //#endif + override val defaultGpuFormatDepth: UGpuFormatImpl = + //#if MC >= 26.2 && !STANDALONE + //$$ gpuFormat(GpuFormat.D32_FLOAT) + //#else + gpuFormat(GL14.GL_DEPTH_COMPONENT32, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT) + //#endif + + //#if MC >= 26.2 && !STANDALONE + //$$ override fun gpuFormat(gpuFormat: GpuFormat): UGpuFormatImpl = + //$$ UGpuFormatImpl(gpuFormat) + //#else + override fun gpuFormat(internalFormat: Int, format: Int, type: Int): UGpuFormatImpl = + UGpuFormatImpl(internalFormat, format, type) + //#endif + + //#if MC >= 1.21.5 && !STANDALONE + //$$ override fun texture(texture: GpuTexture): UGpuTextureImpl = + //$$ UGpuTextureImpl( + //#if MC < 1.21.6 + //$$ UGpuTexture.Usage(-1), + //#endif + //#if MC < 26.2 + //$$ when (texture.format) { + //$$ TextureFormat.RGBA8 -> defaultGpuFormatRgba + //$$ TextureFormat.RED8 -> gpuFormat(GL30.GL_R8, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE) + //#if MC >= 1.21.6 + //$$ TextureFormat.RED8I -> gpuFormat(GL30.GL_R8I, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE) + //#endif + //$$ TextureFormat.DEPTH32 -> defaultGpuFormatDepth + //#if NEOFORGE + //$$ TextureFormat.DEPTH24_STENCIL8 -> gpuFormat(GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8) + //$$ TextureFormat.DEPTH32_STENCIL8 -> gpuFormat(GL30.GL_DEPTH32F_STENCIL8, GL30.GL_DEPTH_STENCIL, GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV) + //#endif + //$$ null -> throw NullPointerException() + //$$ }, + //#endif + //$$ texture, + //$$ ) + //$$ override fun texture(texture: UGpuTexture): GpuTexture = + //$$ texture.impl.mc + //#else + override fun texture(glId: Int, format: UGpuFormat, width: Int, height: Int, mipLevels: Int): UGpuTexture { + require(width > 0) { "Width must be positive" } + require(height > 0) { "Height must be positive" } + require(mipLevels > 0) { "Mip levels must be positive"} + require(mipLevels <= 1) { TODO("mipLevels") } + return UGpuTextureImpl(UGpuTexture.Usage(-1), format.impl, glId, width, height, mipLevels) + } + override fun texture(texture: UGpuTexture): Int = + texture.impl.glId + //#endif + + //#if MC >= 1.21.6 && !STANDALONE + //$$ override fun textureView(textureView: GpuTextureView): UGpuTextureViewImpl = + //$$ UGpuTextureViewImpl(texture(textureView.texture()), textureView) + //$$ override fun textureView(textureView: UGpuTextureView): GpuTextureView = + //$$ textureView.impl.mc + //#endif +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuSampler.kt b/src/main/kotlin/gg/essential/universal/render/UGpuSampler.kt new file mode 100644 index 00000000..34b1f9d4 --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuSampler.kt @@ -0,0 +1,53 @@ +package gg.essential.universal.render + +import org.jetbrains.annotations.ApiStatus.NonExtendable + +//#if MC >= 1.21.11 && !STANDALONE +//$$ import com.mojang.blaze3d.systems.RenderSystem +//#endif + +@NonExtendable +sealed interface UGpuSampler { + enum class AddressMode { + REPEAT, + CLAMP_TO_EDGE, + ; + } + + enum class FilterMode { + NEAREST, + LINEAR, + ; + } + + companion object { + @JvmName("get") + operator fun invoke( + addressModeU: AddressMode, + addressModeV: AddressMode, + minFilter: FilterMode, + magFilter: FilterMode, + useMipmaps: Boolean, + ): UGpuSampler { + //#if MC >= 1.21.11 && !STANDALONE + //$$ return UGpuSamplerImpl(RenderSystem.getSamplerCache() + //$$ .get(addressModeU.mc, addressModeV.mc, minFilter.mc, magFilter.mc, useMipmaps)) + //#else + return UGpuSamplerImpl(addressModeU, addressModeV, minFilter, magFilter, useMipmaps) + //#endif + } + + //#if MC >= 1.21.11 && !STANDALONE + //$$ private val AddressMode.mc: com.mojang.blaze3d.textures.AddressMode + //$$ get() = when (this) { + //$$ AddressMode.REPEAT -> com.mojang.blaze3d.textures.AddressMode.REPEAT + //$$ AddressMode.CLAMP_TO_EDGE -> com.mojang.blaze3d.textures.AddressMode.CLAMP_TO_EDGE + //$$ } + //$$ private val FilterMode.mc: com.mojang.blaze3d.textures.FilterMode + //$$ get() = when (this) { + //$$ FilterMode.NEAREST -> com.mojang.blaze3d.textures.FilterMode.NEAREST + //$$ FilterMode.LINEAR -> com.mojang.blaze3d.textures.FilterMode.LINEAR + //$$ } + //#endif + } +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuSamplerImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuSamplerImpl.kt new file mode 100644 index 00000000..8b8e072b --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuSamplerImpl.kt @@ -0,0 +1,50 @@ +package gg.essential.universal.render + +import gg.essential.universal.render.UGpuSampler.AddressMode +import gg.essential.universal.render.UGpuSampler.FilterMode + +//#if MC >= 1.21.11 && !STANDALONE +//$$ import com.mojang.blaze3d.systems.RenderSystem +//$$ import net.minecraft.client.gl.GpuSampler +//#else +import gg.essential.universal.UGraphics +import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL12 +//#endif + +internal val UGpuSampler.impl: UGpuSamplerImpl + get() = when (this) { is UGpuSamplerImpl -> this } + +//#if MC >= 1.21.11 && !STANDALONE +//$$ internal data class UGpuSamplerImpl(val mc: GpuSampler) : UGpuSampler +//#else +internal data class UGpuSamplerImpl( + val addressModeU: AddressMode, + val addressModeV: AddressMode, + val minFilter: FilterMode, + val magFilter: FilterMode, + val useMipmaps: Boolean, +) : UGpuSampler { + + private val AddressMode.gl: Int + get() = when (this) { + AddressMode.REPEAT -> GL11.GL_REPEAT + AddressMode.CLAMP_TO_EDGE -> GL12.GL_CLAMP_TO_EDGE + } + private val FilterMode.gl: Int + get() = when (this) { + FilterMode.NEAREST -> GL11.GL_NEAREST + FilterMode.LINEAR -> GL11.GL_LINEAR + } + + fun configureTexture(textureGlId: Int) { + UGraphics.configureTexture(textureGlId) { + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, addressModeU.gl) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, addressModeV.gl) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, minFilter.gl) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, magFilter.gl) + GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LOD, if (useMipmaps) 1000f else 0f) + } + } +} +//#endif diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt b/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt new file mode 100644 index 00000000..ffe1a660 --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt @@ -0,0 +1,48 @@ +package gg.essential.universal.render + +import gg.essential.universal.UGraphics +import org.jetbrains.annotations.ApiStatus.NonExtendable +import java.io.Closeable + +@NonExtendable +sealed interface UGpuFormat { + companion object { + val DEFAULT_RGBA: UGpuFormat = + UGraphics.getPlatformAdapter().defaultGpuFormatRgba + val DEFAULT_DEPTH: UGpuFormat = + UGraphics.getPlatformAdapter().defaultGpuFormatDepth + } +} + +@NonExtendable +sealed interface UGpuTexture : Closeable { + val width: Int + val height: Int + val mipLevels: Int + + val isClosed: Boolean + + data class Usage(val bits: Int) { + operator fun contains(other: Usage): Boolean = + (bits and other.bits.inv()) == other.bits + + operator fun plus(other: Usage): Usage = + Usage(bits or other.bits) + + companion object { + val COPY_DST = Usage(1 shl 0) + val COPY_SRC = Usage(1 shl 1) + val TEXTURE_BINDING = Usage(1 shl 2) + val RENDER_ATTACHMENT = Usage(1 shl 3) + } + } +} + +@NonExtendable +sealed interface UGpuTextureView : Closeable { + val texture: UGpuTexture + val baseMipLevel: Int + val mipLevels: Int + + val isClosed: Boolean +} diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuTextureImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuTextureImpl.kt new file mode 100644 index 00000000..dfb6f51c --- /dev/null +++ b/src/main/kotlin/gg/essential/universal/render/UGpuTextureImpl.kt @@ -0,0 +1,160 @@ +package gg.essential.universal.render + +import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL30 + +//#if STANDALONE +//#else +//#if MC >= 26.2 +//$$ import com.mojang.blaze3d.GpuFormat +//#endif +//#if MC >= 1.21.6 +//$$ import com.mojang.blaze3d.textures.GpuTextureView +//#endif +//#if MC >= 1.21.5 +//$$ import com.mojang.blaze3d.textures.GpuTexture +//#endif + +import net.minecraft.client.renderer.GlStateManager +//#endif + +internal data class UGpuFormatImpl( + //#if MC >= 26.2 && !STANDALONE + //$$ val mc: GpuFormat, + //#else + val internalFormat: Int, + val format: Int, + val type: Int, + //#endif +) : UGpuFormat { + val hasColor: Boolean + //#if MC >= 26.2 && !STANDALONE + //$$ get() = mc.hasColorAspect() + //#else + get() = when (format) { + GL11.GL_STENCIL_INDEX, GL11.GL_DEPTH_COMPONENT, GL30.GL_DEPTH_STENCIL -> false + else -> true + } + //#endif + val hasDepth: Boolean + //#if MC >= 26.2 && !STANDALONE + //$$ get() = mc.hasDepthAspect() + //#else + get() = when (format) { + GL11.GL_DEPTH_COMPONENT, GL30.GL_DEPTH_STENCIL -> true + else -> false + } + //#endif +} + +internal class UGpuTextureImpl( + //#if MC < 1.21.6 || STANDALONE + val usage: UGpuTexture.Usage, + //#endif + //#if MC < 26.2 || STANDALONE + val format: UGpuFormatImpl, + //#endif + //#if MC >= 1.21.5 && !STANDALONE + //$$ val mc: GpuTexture, + //#else + val glId: Int, + override val width: Int, + override val height: Int, + override val mipLevels: Int + //#endif +) : UGpuTexture { + + //#if MC >= 1.21.6 && !STANDALONE + //$$ val usage: UGpuTexture.Usage + //$$ get() = UGpuTexture.Usage(mc.usage()) + //#endif + + //#if MC >= 26.2 && !STANDALONE + //$$ val format: UGpuFormatImpl + //$$ get() = UGpuFormatImpl(mc.format) + //#endif + + //#if MC >= 1.21.5 && !STANDALONE + //$$ override val width: Int get() = mc.getWidth(0) + //$$ override val height: Int get() = mc.getHeight(0) + //$$ override val mipLevels: Int get() = mc.mipLevels + //#endif + + //#if MC >= 1.21.6 && !STANDALONE + //$$ override val isClosed: Boolean get() = mc.isClosed + //$$ override fun close() = mc.close() + //#else + private var refCount = 1 + + internal fun increaseRefCount() { + refCount++ + } + + internal fun decreaseRefCount() { + refCount-- + + if (refCount == 0) { + free() + } + } + + private fun free() { + //#if STANDALONE + //$$ GL11.glDeleteTextures(glId) + //#elseif MC >= 1.21.5 + //$$ mc.close() + //#else + GlStateManager.deleteTexture(glId) + //#endif + } + + override var isClosed = false + private set + + override fun close() { + if (!isClosed) { + isClosed = true + decreaseRefCount() + } + } + //#endif +} + +//#if MC >= 1.21.6 && !STANDALONE +//$$ internal class UGpuTextureViewImpl( +//$$ override val texture: UGpuTextureImpl, +//$$ val mc: GpuTextureView, +//$$ ) : UGpuTextureView { +//$$ override val baseMipLevel: Int get() = mc.baseMipLevel() +//$$ override val mipLevels: Int get() = mc.mipLevels() +//$$ override val isClosed: Boolean get() = mc.isClosed +//$$ override fun close() = mc.close() +//$$ } +//#else +internal class UGpuTextureViewImpl( + override val texture: UGpuTextureImpl, + override val baseMipLevel: Int, + override val mipLevels: Int, +) : UGpuTextureView { + init { + texture.increaseRefCount() + } + + override var isClosed: Boolean = false + private set + + override fun close() { + if (!isClosed) { + isClosed = true + texture.decreaseRefCount() + } + } +} +//#endif + +internal val UGpuFormat.impl: UGpuFormatImpl + get() = when (this) { is UGpuFormatImpl -> this } +internal val UGpuTexture.impl: UGpuTextureImpl + get() = when (this) { is UGpuTextureImpl -> this } +internal val UGpuTextureView.impl: UGpuTextureViewImpl + get() = when (this) { is UGpuTextureViewImpl -> this } diff --git a/src/main/kotlin/gg/essential/universal/render/URenderPass.kt b/src/main/kotlin/gg/essential/universal/render/URenderPass.kt index 47bbd058..bdc24df2 100644 --- a/src/main/kotlin/gg/essential/universal/render/URenderPass.kt +++ b/src/main/kotlin/gg/essential/universal/render/URenderPass.kt @@ -165,6 +165,33 @@ internal class URenderPass : AutoCloseable { //#endif } + override fun texture(name: String, textureView: UGpuTextureView, sampler: UGpuSampler): DrawCallBuilder = apply { + //#if MC >= 1.21.5 && !STANDALONE + //#if MC >= 1.21.11 + //$$ mc.bindTexture(name, textureView.impl.mc, sampler.impl.mc) + //#elseif MC >= 1.21.6 + //$$ sampler.impl.configureTexture((textureView.texture.impl.mc as GlTexture).glId) + //$$ mc.bindSampler(name, textureView.impl.mc) + //#else + //$$ sampler.impl.configureTexture((textureView.texture.impl.mc as GlTexture).glId) + //$$ mc.bindSampler(name, textureView.texture.impl.mc) + //#endif + //#else + sampler.impl.configureTexture(textureView.texture.impl.glId) + pipeline.texture(name, textureView.texture.impl.glId) + //#endif + } + + override fun texture(index: Int, textureView: UGpuTextureView, sampler: UGpuSampler): DrawCallBuilder = apply { + //#if MC >= 1.21.5 && !STANDALONE + //$$ texture(pipeline.mcRenderPipeline.samplers[index], textureView, sampler) + //#else + sampler.impl.configureTexture(textureView.texture.impl.glId) + pipeline.texture(index, textureView.texture.impl.glId) + //#endif + } + + @Deprecated("Does not support Vulkan; uses hard-coded sampler on 1.21.11+, relies on texture configuration on older versions.") override fun texture(name: String, textureGlId: Int): DrawCallBuilder = apply { //#if MC>=12105 && !STANDALONE //#if MC>=12106 @@ -191,8 +218,10 @@ internal class URenderPass : AutoCloseable { //#endif } + @Deprecated("Does not support Vulkan; uses hard-coded sampler on 1.21.11+, relies on texture configuration on older versions.") override fun texture(index: Int, textureGlId: Int): DrawCallBuilder = apply { //#if MC>=12105 && !STANDALONE + //$$ @Suppress("DEPRECATION") //$$ texture(pipeline.mcRenderPipeline.samplers[index], textureGlId) //#else pipeline.texture(index, textureGlId) diff --git a/src/main/kotlin/gg/essential/universal/utils/ReleasedDynamicTexture.kt b/src/main/kotlin/gg/essential/universal/utils/ReleasedDynamicTexture.kt index a8bace89..db017788 100644 --- a/src/main/kotlin/gg/essential/universal/utils/ReleasedDynamicTexture.kt +++ b/src/main/kotlin/gg/essential/universal/utils/ReleasedDynamicTexture.kt @@ -1,6 +1,10 @@ package gg.essential.universal.utils import gg.essential.universal.UGraphics +import gg.essential.universal.render.UGpuFormat +import gg.essential.universal.render.UGpuTexture +import gg.essential.universal.render.UGpuTextureView +import gg.essential.universal.render.impl //#if STANDALONE //$$ import org.lwjgl.BufferUtils @@ -94,10 +98,16 @@ class ReleasedDynamicTexture private constructor( fun uploadTexture() { if (!uploaded) { + val texture = UGraphics.getDevice().createTexture( + null, + UGpuTexture.Usage.TEXTURE_BINDING + UGpuTexture.Usage.COPY_SRC + UGpuTexture.Usage.COPY_DST, + UGpuFormat.DEFAULT_RGBA, + width, + height, + ).impl + //#if STANDALONE - //$$ val glId = GL20C.glGenTextures() - //$$ - //$$ GL20C.glBindTexture(GL20C.GL_TEXTURE_2D, glId) + //$$ GL20C.glBindTexture(GL20C.GL_TEXTURE_2D, texture.glId) //$$ //$$ GL20C.glTexParameteri(GL20C.GL_TEXTURE_2D, GL20C.GL_TEXTURE_MIN_FILTER, GL20C.GL_LINEAR) //$$ GL20C.glTexParameteri(GL20C.GL_TEXTURE_2D, GL20C.GL_TEXTURE_MAG_FILTER, GL20C.GL_NEAREST) @@ -118,74 +128,70 @@ class ReleasedDynamicTexture private constructor( //$$ GL20C.GL_UNSIGNED_BYTE, //$$ nativeBuffer //$$ ) - //$$ textureData = IntArray(0) - //$$ - //$$ uploaded = true - //$$ resources.glId = glId //#elseif MC>=12105 //$$ val device = RenderSystem.getDevice() - //#if MC>=12106 - //$$ val usage = GpuTexture.USAGE_TEXTURE_BINDING or GpuTexture.USAGE_COPY_SRC or GpuTexture.USAGE_COPY_DST - //$$ val texture = device.createTexture(null as String?, usage, TextureFormat.RGBA8, width, height, 1, 1) - //#else - //$$ val texture = device.createTexture(null as String?, TextureFormat.RGBA8, width, height, 1) - //#endif //#if MC>=12111 //$$ sampler = RenderSystem.getSamplerCache().get(AddressMode.REPEAT, AddressMode.REPEAT, FilterMode.LINEAR, FilterMode.NEAREST, true); //#else - //$$ texture.setTextureFilter(FilterMode.NEAREST, true) - //$$ UGraphics.configureTexture((texture as GlTexture).glId) { + //$$ texture.mc.setTextureFilter(FilterMode.NEAREST, true) + //$$ UGraphics.configureTexture((texture.mc as GlTexture).glId) { //#if MC>=12106 - //$$ texture.checkDirty(GL11.GL_TEXTURE_2D) + //$$ texture.mc.checkDirty(GL11.GL_TEXTURE_2D) //#else - //$$ texture.checkDirty() + //$$ texture.mc.checkDirty() //#endif //$$ } //#endif - //$$ device.createCommandEncoder().writeToTexture(texture, textureData!!) - //$$ textureData = null - //$$ uploaded = true - //$$ resources.gpuTexture = texture - //$$ this.glTexture = texture - //#if MC>=12106 - //$$ val view = device.createTextureView(texture) - //$$ resources.gpuTextureView = view - //$$ this.glTextureView = view - //#endif + //$$ device.createCommandEncoder().writeToTexture(texture.mc, textureData!!) + //$$ this.glTexture = texture.mc //#else - TextureUtil.allocateTexture(allocGlId(), width, height) - //#if MC>=11400 - //$$ UGraphics.configureTexture(allocGlId()) { + //$$ UGraphics.configureTexture(texture.glId) { //$$ textureData?.uploadTextureSub(0, 0, 0, false) //$$ GlStateManager.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) //$$ GlStateManager.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST) //$$ } - //$$ textureData = null //#else TextureUtil.uploadTexture( - super.getGlTextureId(), textureData, + texture.glId, textureData, width, height ) + //#endif + glTextureId = texture.glId + //#endif + + val textureView = UGraphics.getDevice().createTextureView(texture) + resources.gpuTextureView = textureView + //#if MC>=12106 && !STANDALONE + //$$ this.glTextureView = textureView.impl.mc + //#endif + + //#if MC>=11400 && !STANDALONE + //$$ textureData = null + //#else textureData = IntArray(0) //#endif + uploaded = true - resources.glId = allocGlId() - //#endif Resources.drainCleanupQueue() } } - //#if MC<12105 - private fun allocGlId() = super.getGlTextureId() - //#endif + val gpuTexture: UGpuTexture + get() = gpuTextureView.texture + + val gpuTextureView: UGpuTextureView + get() { + uploadTexture() + return resources.gpuTextureView ?: throw IllegalStateException("Texture has been closed.") + } val dynamicGlId: Int //#if MC>=12105 && !STANDALONE //$$ get() { //$$ uploadTexture() - //$$ return (resources.gpuTexture as GlTexture?)?.glId ?: -1 + //$$ return (resources.gpuTextureView?.texture?.impl?.mc as GlTexture?)?.glId ?: -1 //$$ } //#else get() = getGlTextureId() @@ -194,12 +200,11 @@ class ReleasedDynamicTexture private constructor( //#if STANDALONE //$$ fun getGlTextureId(): Int { //$$ uploadTexture() - //$$ return resources.glId + //$$ return resources.gpuTextureView?.texture?.impl?.glId ?: -1 //$$ } //$$ //$$ fun deleteGlTexture() { - //$$ UGraphics.deleteTexture(resources.glId) - //$$ resources.glId = -1 + //$$ resources.gpuTextureView = null //$$ } //#elseif MC>=12105 //#if MC>=12106 @@ -240,7 +245,7 @@ class ReleasedDynamicTexture private constructor( override fun deleteGlTexture() { super.deleteGlTexture() - resources.glId = -1 + resources.gpuTextureView = null } //#endif @@ -258,23 +263,12 @@ class ReleasedDynamicTexture private constructor( //#endif private class Resources(referent: ReleasedDynamicTexture) : PhantomReference(referent, referenceQueue), Closeable { - //#if MC>=12105 && !STANDALONE - //$$ var gpuTexture: GpuTexture? = null - //$$ set(value) { - //$$ field?.close() - //$$ field = value - //$$ } - //$$ - //#if MC>=12106 - //$$ var gpuTextureView: GpuTextureView? = null - //$$ set(value) { - //$$ field?.close() - //$$ field = value - //$$ } - //#endif - //#else - var glId: Int = -1 - //#endif + var gpuTextureView: UGpuTextureView? = null + set(value) { + field?.texture?.close() + field?.close() + field = value + } //#if MC>=11400 && !STANDALONE //$$ var textureData: NativeImage? = null @@ -291,17 +285,7 @@ class ReleasedDynamicTexture private constructor( override fun close() { toBeCleanedUp.remove(this) - //#if MC>=12105 && !STANDALONE - //$$ gpuTexture = null - //#if MC>=12106 - //$$ gpuTextureView = null - //#endif - //#else - if (glId != -1) { - UGraphics.deleteTexture(glId) - glId = -1 - } - //#endif + gpuTextureView = null //#if MC>=11400 && !STANDALONE //$$ textureData = null diff --git a/versions/1.17.1-1.16.2.txt b/versions/1.17.1-1.16.2.txt index 8a73e7dd..18604f8b 100644 --- a/versions/1.17.1-1.16.2.txt +++ b/versions/1.17.1-1.16.2.txt @@ -13,4 +13,5 @@ com.mojang.blaze3d.platform.GlStateManager glGetProgrami() getProgram() com.mojang.blaze3d.platform.GlStateManager glGetProgramInfoLog() getProgramInfoLog() com.mojang.blaze3d.platform.GlStateManager _texParameter() texParameter() com.mojang.blaze3d.platform.GlStateManager _logicOp() logicOp() +com.mojang.blaze3d.platform.GlStateManager _genTexture() genTextures() com.mojang.blaze3d.systems.RenderSystem setShaderColor() color4f() From 5a2869e4b21c519b564216e715ffefea72eb43d1 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 12 May 2026 09:08:43 +0200 Subject: [PATCH 2/3] dedup texture size checks, fix mipmap TODO --- .../essential/universal/render/UGpuDeviceImpl.kt | 16 ++++++++++------ .../universal/render/UGpuPlatformAdapterImpl.kt | 6 ++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt index 396ead4f..af000969 100644 --- a/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt +++ b/src/main/kotlin/gg/essential/universal/render/UGpuDeviceImpl.kt @@ -34,12 +34,7 @@ internal object UGpuDeviceImpl : UGpuDevice { mipLevels: Int ): UGpuTexture { require(usage.bits != 0) { "At least one usage bit must be set" } - require(width > 0) { "Width must be positive but was $width" } - require(height > 0) { "Height must be positive but was $height" } - require(mipLevels > 0) { "Mip levels must be positive but was $mipLevels"} - fun log2(x: Int) = 31 - x.countLeadingZeroBits() - val maxMipLevels = log2(max(width, height)) + 1 - require(mipLevels <= maxMipLevels) { "Texture of size ${width}x${height} supports at most $maxMipLevels but $mipLevels were requested" } + requireValidTextureSize(width, height, mipLevels) //#if STANDALONE //$$ return createGlTexture(label, usage, format.impl, width, height, mipLevels) @@ -188,4 +183,13 @@ internal object UGpuDeviceImpl : UGpuDevice { return UGpuTextureViewImpl(texture.impl, baseMipLevel, mipLevels) //#endif } + + internal fun requireValidTextureSize(width: Int, height: Int, mipLevels: Int) { + require(width > 0) { "Width must be positive but was $width" } + require(height > 0) { "Height must be positive but was $height" } + require(mipLevels > 0) { "Mip levels must be positive but was $mipLevels"} + fun log2(x: Int) = 31 - x.countLeadingZeroBits() + val maxMipLevels = log2(max(width, height)) + 1 + require(mipLevels <= maxMipLevels) { "Texture of size ${width}x${height} supports at most $maxMipLevels but $mipLevels were requested" } + } } diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt index b8df9f62..aff263b0 100644 --- a/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt +++ b/src/main/kotlin/gg/essential/universal/render/UGpuPlatformAdapterImpl.kt @@ -1,5 +1,6 @@ package gg.essential.universal.render +import gg.essential.universal.render.UGpuDeviceImpl.requireValidTextureSize import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL14 @@ -64,10 +65,7 @@ internal object UGpuPlatformAdapterImpl : UGpuPlatformAdapter { //$$ texture.impl.mc //#else override fun texture(glId: Int, format: UGpuFormat, width: Int, height: Int, mipLevels: Int): UGpuTexture { - require(width > 0) { "Width must be positive" } - require(height > 0) { "Height must be positive" } - require(mipLevels > 0) { "Mip levels must be positive"} - require(mipLevels <= 1) { TODO("mipLevels") } + requireValidTextureSize(width, height, mipLevels) return UGpuTextureImpl(UGpuTexture.Usage(-1), format.impl, glId, width, height, mipLevels) } override fun texture(texture: UGpuTexture): Int = From 701d23c130014c2fef1827ae3dfc610b4ced3d19 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 12 May 2026 09:10:02 +0200 Subject: [PATCH 3/3] fix Usage.contains bit logic --- src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt b/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt index ffe1a660..f9ad4e04 100644 --- a/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt +++ b/src/main/kotlin/gg/essential/universal/render/UGpuTexture.kt @@ -24,7 +24,7 @@ sealed interface UGpuTexture : Closeable { data class Usage(val bits: Int) { operator fun contains(other: Usage): Boolean = - (bits and other.bits.inv()) == other.bits + (bits and other.bits) == other.bits operator fun plus(other: Usage): Usage = Usage(bits or other.bits)