446 lines
19 KiB
Java
446 lines
19 KiB
Java
/*
|
|
* 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<RenderPipeline, GlRenderPipeline> pipelineCache = new IdentityHashMap<RenderPipeline, GlRenderPipeline>();
|
|
private final Map<ShaderCompilationKey, GlShaderModule> shaderCache = new HashMap<ShaderCompilationKey, GlShaderModule>();
|
|
private final VertexArrayCache vertexArrayCache;
|
|
private final BufferStorage bufferStorage;
|
|
private final Set<String> enabledExtensions = new HashSet<String>();
|
|
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<String> 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<String> 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<String> 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<String> 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<String> getEnabledExtensions() {
|
|
return new ArrayList<String>(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;
|
|
}
|
|
}
|
|
}
|
|
|