/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.mojang.logging.LogUtils * org.apache.commons.lang3.StringUtils * org.jspecify.annotations.Nullable * org.lwjgl.glfw.GLFW * org.lwjgl.opengl.GL * org.lwjgl.opengl.GL11 * org.lwjgl.opengl.GLCapabilities * org.slf4j.Logger */ package com.mojang.blaze3d.opengl; import com.mojang.blaze3d.GpuOutOfMemoryException; import com.mojang.blaze3d.GraphicsWorkarounds; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.opengl.BufferStorage; import com.mojang.blaze3d.opengl.DirectStateAccess; import com.mojang.blaze3d.opengl.GlBuffer; import com.mojang.blaze3d.opengl.GlCommandEncoder; import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlDebug; import com.mojang.blaze3d.opengl.GlDebugLabel; import com.mojang.blaze3d.opengl.GlProgram; import com.mojang.blaze3d.opengl.GlRenderPipeline; import com.mojang.blaze3d.opengl.GlSampler; import com.mojang.blaze3d.opengl.GlShaderModule; import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.opengl.GlTextureView; import com.mojang.blaze3d.opengl.VertexArrayCache; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.preprocessor.GlslPreprocessor; import com.mojang.blaze3d.shaders.ShaderSource; import com.mojang.blaze3d.shaders.ShaderType; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.textures.AddressMode; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuSampler; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.textures.TextureFormat; import com.mojang.logging.LogUtils; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.OptionalDouble; import java.util.Set; import java.util.function.Supplier; import net.minecraft.client.renderer.ShaderDefines; import net.minecraft.client.renderer.ShaderManager; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import org.apache.commons.lang3.StringUtils; import org.jspecify.annotations.Nullable; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GLCapabilities; import org.slf4j.Logger; public class GlDevice implements GpuDevice { private static final Logger LOGGER = LogUtils.getLogger(); protected static boolean USE_GL_ARB_vertex_attrib_binding = true; protected static boolean USE_GL_KHR_debug = true; protected static boolean USE_GL_EXT_debug_label = true; protected static boolean USE_GL_ARB_debug_output = true; protected static boolean USE_GL_ARB_direct_state_access = true; protected static boolean USE_GL_ARB_buffer_storage = true; private final CommandEncoder encoder; private final @Nullable GlDebug debugLog; private final GlDebugLabel debugLabels; private final int maxSupportedTextureSize; private final DirectStateAccess directStateAccess; private final ShaderSource defaultShaderSource; private final Map pipelineCache = new IdentityHashMap(); private final Map shaderCache = new HashMap(); private final VertexArrayCache vertexArrayCache; private final BufferStorage bufferStorage; private final Set enabledExtensions = new HashSet(); private final int uniformOffsetAlignment; private final int maxSupportedAnisotropy; public GlDevice(long windowHandle, int debugLevel, boolean synchronousLogs, ShaderSource defaultShaderSource, boolean wantsDebugLabels) { GLFW.glfwMakeContextCurrent((long)windowHandle); GLCapabilities capabilities = GL.createCapabilities(); int maxSize = GlDevice.getMaxSupportedTextureSize(); GLFW.glfwSetWindowSizeLimits((long)windowHandle, (int)-1, (int)-1, (int)maxSize, (int)maxSize); GraphicsWorkarounds workarounds = GraphicsWorkarounds.get(this); this.debugLog = GlDebug.enableDebugCallback(debugLevel, synchronousLogs, this.enabledExtensions); this.debugLabels = GlDebugLabel.create(capabilities, wantsDebugLabels, this.enabledExtensions); this.vertexArrayCache = VertexArrayCache.create(capabilities, this.debugLabels, this.enabledExtensions); this.bufferStorage = BufferStorage.create(capabilities, this.enabledExtensions); this.directStateAccess = DirectStateAccess.create(capabilities, this.enabledExtensions, workarounds); this.maxSupportedTextureSize = maxSize; this.defaultShaderSource = defaultShaderSource; this.encoder = new GlCommandEncoder(this); this.uniformOffsetAlignment = GL11.glGetInteger((int)35380); GL11.glEnable((int)34895); GL11.glEnable((int)34370); if (capabilities.GL_EXT_texture_filter_anisotropic) { this.maxSupportedAnisotropy = Mth.floor(GL11.glGetFloat((int)34047)); this.enabledExtensions.add("GL_EXT_texture_filter_anisotropic"); } else { this.maxSupportedAnisotropy = 1; } } public GlDebugLabel debugLabels() { return this.debugLabels; } @Override public CommandEncoder createCommandEncoder() { return this.encoder; } @Override public int getMaxSupportedAnisotropy() { return this.maxSupportedAnisotropy; } @Override public GpuSampler createSampler(AddressMode addressModeU, AddressMode addressModeV, FilterMode minFilter, FilterMode magFilter, int maxAnisotropy, OptionalDouble maxLod) { if (maxAnisotropy < 1 || maxAnisotropy > this.maxSupportedAnisotropy) { throw new IllegalArgumentException("maxAnisotropy out of range; must be >= 1 and <= " + this.getMaxSupportedAnisotropy() + ", but was " + maxAnisotropy); } return new GlSampler(addressModeU, addressModeV, minFilter, magFilter, maxAnisotropy, maxLod); } @Override public GpuTexture createTexture(@Nullable Supplier label, @GpuTexture.Usage int usage, TextureFormat format, int width, int height, int depthOrLayers, int mipLevels) { return this.createTexture(this.debugLabels.exists() && label != null ? label.get() : null, usage, format, width, height, depthOrLayers, mipLevels); } @Override public GpuTexture createTexture(@Nullable String label, @GpuTexture.Usage int usage, TextureFormat format, int width, int height, int depthOrLayers, int mipLevels) { int error; int target; boolean isCubemap; if (mipLevels < 1) { throw new IllegalArgumentException("mipLevels must be at least 1"); } if (depthOrLayers < 1) { throw new IllegalArgumentException("depthOrLayers must be at least 1"); } boolean bl = isCubemap = (usage & 0x10) != 0; if (isCubemap) { if (width != height) { throw new IllegalArgumentException("Cubemap compatible textures must be square, but size is " + width + "x" + height); } if (depthOrLayers % 6 != 0) { throw new IllegalArgumentException("Cubemap compatible textures must have a layer count with a multiple of 6, was " + depthOrLayers); } if (depthOrLayers > 6) { throw new UnsupportedOperationException("Array textures are not yet supported"); } } else if (depthOrLayers > 1) { throw new UnsupportedOperationException("Array or 3D textures are not yet supported"); } GlStateManager.clearGlErrors(); int id = GlStateManager._genTexture(); if (label == null) { label = String.valueOf(id); } if (isCubemap) { GL11.glBindTexture((int)34067, (int)id); target = 34067; } else { GlStateManager._bindTexture(id); target = 3553; } GlStateManager._texParameter(target, 33085, mipLevels - 1); GlStateManager._texParameter(target, 33082, 0); GlStateManager._texParameter(target, 33083, mipLevels - 1); if (format.hasDepthAspect()) { GlStateManager._texParameter(target, 34892, 0); } if (isCubemap) { for (int cubeTarget : GlConst.CUBEMAP_TARGETS) { for (int i = 0; i < mipLevels; ++i) { GlStateManager._texImage2D(cubeTarget, i, GlConst.toGlInternalId(format), width >> i, height >> i, 0, GlConst.toGlExternalId(format), GlConst.toGlType(format), null); } } } else { for (int i = 0; i < mipLevels; ++i) { GlStateManager._texImage2D(target, i, GlConst.toGlInternalId(format), width >> i, height >> i, 0, GlConst.toGlExternalId(format), GlConst.toGlType(format), null); } } if ((error = GlStateManager._getError()) == 1285) { throw new GpuOutOfMemoryException("Could not allocate texture of " + width + "x" + height + " for " + label); } if (error != 0) { throw new IllegalStateException("OpenGL error " + error); } GlTexture texture = new GlTexture(usage, label, format, width, height, depthOrLayers, mipLevels, id); this.debugLabels.applyLabel(texture); return texture; } @Override public GpuTextureView createTextureView(GpuTexture texture) { return this.createTextureView(texture, 0, texture.getMipLevels()); } @Override public GpuTextureView createTextureView(GpuTexture texture, int baseMipLevel, int mipLevels) { if (texture.isClosed()) { throw new IllegalArgumentException("Can't create texture view with closed texture"); } if (baseMipLevel < 0 || baseMipLevel + mipLevels > texture.getMipLevels()) { throw new IllegalArgumentException(mipLevels + " mip levels starting from " + baseMipLevel + " would be out of range for texture with only " + texture.getMipLevels() + " mip levels"); } return new GlTextureView((GlTexture)texture, baseMipLevel, mipLevels); } @Override public GpuBuffer createBuffer(@Nullable Supplier label, @GpuBuffer.Usage int usage, int size) { if (size <= 0) { throw new IllegalArgumentException("Buffer size must be greater than zero"); } GlStateManager.clearGlErrors(); GlBuffer buffer = this.bufferStorage.createBuffer(this.directStateAccess, label, usage, size); int error = GlStateManager._getError(); if (error == 1285) { throw new GpuOutOfMemoryException("Could not allocate buffer of " + size + " for " + String.valueOf(label)); } if (error != 0) { throw new IllegalStateException("OpenGL error " + error); } this.debugLabels.applyLabel(buffer); return buffer; } @Override public GpuBuffer createBuffer(@Nullable Supplier label, @GpuBuffer.Usage int usage, ByteBuffer data) { if (!data.hasRemaining()) { throw new IllegalArgumentException("Buffer source must not be empty"); } GlStateManager.clearGlErrors(); long size = data.remaining(); GlBuffer buffer = this.bufferStorage.createBuffer(this.directStateAccess, label, usage, data); int error = GlStateManager._getError(); if (error == 1285) { throw new GpuOutOfMemoryException("Could not allocate buffer of " + size + " for " + String.valueOf(label)); } if (error != 0) { throw new IllegalStateException("OpenGL error " + error); } this.debugLabels.applyLabel(buffer); return buffer; } @Override public String getImplementationInformation() { if (GLFW.glfwGetCurrentContext() == 0L) { return "NO CONTEXT"; } return GlStateManager._getString(7937) + " GL version " + GlStateManager._getString(7938) + ", " + GlStateManager._getString(7936); } @Override public List getLastDebugMessages() { return this.debugLog == null ? Collections.emptyList() : this.debugLog.getLastOpenGlDebugMessages(); } @Override public boolean isDebuggingEnabled() { return this.debugLog != null; } @Override public String getRenderer() { return GlStateManager._getString(7937); } @Override public String getVendor() { return GlStateManager._getString(7936); } @Override public String getBackendName() { return "OpenGL"; } @Override public String getVersion() { return GlStateManager._getString(7938); } private static int getMaxSupportedTextureSize() { int maxReported = GlStateManager._getInteger(3379); for (int texSize = Math.max(32768, maxReported); texSize >= 1024; texSize >>= 1) { GlStateManager._texImage2D(32868, 0, 6408, texSize, texSize, 0, 6408, 5121, null); int width = GlStateManager._getTexLevelParameter(32868, 0, 4096); if (width == 0) continue; return texSize; } int maxSupportedTextureSize = Math.max(maxReported, 1024); LOGGER.info("Failed to determine maximum texture size by probing, trying GL_MAX_TEXTURE_SIZE = {}", (Object)maxSupportedTextureSize); return maxSupportedTextureSize; } @Override public int getMaxTextureSize() { return this.maxSupportedTextureSize; } @Override public int getUniformOffsetAlignment() { return this.uniformOffsetAlignment; } @Override public void clearPipelineCache() { for (GlRenderPipeline pipeline : this.pipelineCache.values()) { if (pipeline.program() == GlProgram.INVALID_PROGRAM) continue; pipeline.program().close(); } this.pipelineCache.clear(); for (GlShaderModule shader : this.shaderCache.values()) { if (shader == GlShaderModule.INVALID_SHADER) continue; shader.close(); } this.shaderCache.clear(); String glRenderer = GlStateManager._getString(7937); if (glRenderer.contains("AMD")) { GlDevice.sacrificeShaderToOpenGlAndAmd(); } } private static void sacrificeShaderToOpenGlAndAmd() { int shader = GlStateManager.glCreateShader(35633); int program = GlStateManager.glCreateProgram(); GlStateManager.glAttachShader(program, shader); GlStateManager.glDeleteShader(shader); GlStateManager.glDeleteProgram(program); } @Override public List getEnabledExtensions() { return new ArrayList(this.enabledExtensions); } @Override public void close() { this.clearPipelineCache(); } public DirectStateAccess directStateAccess() { return this.directStateAccess; } protected GlRenderPipeline getOrCompilePipeline(RenderPipeline pipeline) { return this.pipelineCache.computeIfAbsent(pipeline, p -> this.compilePipeline((RenderPipeline)p, this.defaultShaderSource)); } protected GlShaderModule getOrCompileShader(Identifier id, ShaderType type, ShaderDefines defines, ShaderSource shaderSource) { ShaderCompilationKey key = new ShaderCompilationKey(id, type, defines); return this.shaderCache.computeIfAbsent(key, k -> this.compileShader((ShaderCompilationKey)k, shaderSource)); } @Override public GlRenderPipeline precompilePipeline(RenderPipeline pipeline, @Nullable ShaderSource customShaderSource) { ShaderSource shaderSource = customShaderSource == null ? this.defaultShaderSource : customShaderSource; return this.pipelineCache.computeIfAbsent(pipeline, p -> this.compilePipeline((RenderPipeline)p, shaderSource)); } private GlShaderModule compileShader(ShaderCompilationKey key, ShaderSource shaderSource) { String source = shaderSource.get(key.id, key.type); if (source == null) { LOGGER.error("Couldn't find source for {} shader ({})", (Object)key.type, (Object)key.id); return GlShaderModule.INVALID_SHADER; } String sourceWithDefines = GlslPreprocessor.injectDefines(source, key.defines); int shaderId = GlStateManager.glCreateShader(GlConst.toGl(key.type)); GlStateManager.glShaderSource(shaderId, sourceWithDefines); GlStateManager.glCompileShader(shaderId); if (GlStateManager.glGetShaderi(shaderId, 35713) == 0) { String logInfo = StringUtils.trim((String)GlStateManager.glGetShaderInfoLog(shaderId, 32768)); LOGGER.error("Couldn't compile {} shader ({}): {}", new Object[]{key.type.getName(), key.id, logInfo}); return GlShaderModule.INVALID_SHADER; } GlShaderModule module = new GlShaderModule(shaderId, key.id, key.type); this.debugLabels.applyLabel(module); return module; } private GlProgram compileProgram(RenderPipeline pipeline, ShaderSource shaderSource) { GlShaderModule vertexShader = this.getOrCompileShader(pipeline.getVertexShader(), ShaderType.VERTEX, pipeline.getShaderDefines(), shaderSource); GlShaderModule fragmentShader = this.getOrCompileShader(pipeline.getFragmentShader(), ShaderType.FRAGMENT, pipeline.getShaderDefines(), shaderSource); if (vertexShader == GlShaderModule.INVALID_SHADER) { LOGGER.error("Couldn't compile pipeline {}: vertex shader {} was invalid", (Object)pipeline.getLocation(), (Object)pipeline.getVertexShader()); return GlProgram.INVALID_PROGRAM; } if (fragmentShader == GlShaderModule.INVALID_SHADER) { LOGGER.error("Couldn't compile pipeline {}: fragment shader {} was invalid", (Object)pipeline.getLocation(), (Object)pipeline.getFragmentShader()); return GlProgram.INVALID_PROGRAM; } try { GlProgram compiled = GlProgram.link(vertexShader, fragmentShader, pipeline.getVertexFormat(), pipeline.getLocation().toString()); compiled.setupUniforms(pipeline.getUniforms(), pipeline.getSamplers()); this.debugLabels.applyLabel(compiled); return compiled; } catch (ShaderManager.CompilationException e) { LOGGER.error("Couldn't compile program for pipeline {}: {}", (Object)pipeline.getLocation(), (Object)e); return GlProgram.INVALID_PROGRAM; } } private GlRenderPipeline compilePipeline(RenderPipeline pipeline, ShaderSource shaderSource) { return new GlRenderPipeline(pipeline, this.compileProgram(pipeline, shaderSource)); } public VertexArrayCache vertexArrayCache() { return this.vertexArrayCache; } public BufferStorage getBufferStorage() { return this.bufferStorage; } private record ShaderCompilationKey(Identifier id, ShaderType type, ShaderDefines defines) { @Override public String toString() { String string = String.valueOf(this.id) + " (" + String.valueOf((Object)this.type) + ")"; if (!this.defines.isEmpty()) { return string + " with " + String.valueOf(this.defines); } return string; } } }