fix(render): skip shaders that fail to compile instead of crashing
ShaderProgram now logs and disables itself (valid=false) on shader compile/link failure instead of throwing IllegalStateException. The throw previously bubbled out of StencilHelper's static initializer as ExceptionInInitializerError and crashed the ClickGUI on drivers that reject legacy GLSL (Intel forward-compatible GL contexts: 'texture2D removed in Forward Compatible context'). Callers degrade gracefully when a shader is unavailable: use() is a no-op, drawRoundedRect falls back to a plain rect, drawBlurredRect disables blur for the session, and StencilHelper.applyStencil draws content directly without the rounded composite. Closes #20, #24 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,12 @@ public class StencilHelper {
|
||||
private static final RenderTarget mainRenderTarget = ClientBase.mc.getMainRenderTarget();
|
||||
|
||||
public static void applyStencil(PoseStack poseStack, Runnable drawMask, Runnable drawContent, float opacity) {
|
||||
if (!stencilShader.isValid()) {
|
||||
// Stencil shader unavailable on this driver — skip the rounded composite and
|
||||
// just draw the content directly so the panel still shows (without rounding).
|
||||
drawContent.run();
|
||||
return;
|
||||
}
|
||||
Matrix4f pose = poseStack.last().pose();
|
||||
RenderTarget mask = maskTarget.get();
|
||||
if (mask.width != StencilHelper.mainRenderTarget.width || mask.height != StencilHelper.mainRenderTarget.height) {
|
||||
|
||||
@@ -19,6 +19,8 @@ public class ShaderProgram {
|
||||
@Getter
|
||||
private final int programId;
|
||||
@Getter
|
||||
private final boolean valid;
|
||||
@Getter
|
||||
private final Map<String, Integer> uniformCache = new HashMap<>();
|
||||
@Getter
|
||||
private final Matrix4Uniform modelViewUniform;
|
||||
@@ -50,6 +52,18 @@ public class ShaderProgram {
|
||||
this.programId = GL20.glCreateProgram();
|
||||
int fragmentShader = ShaderProgram.compileShader(ShaderSource.getByFileName(fragmentName + ".fsh").getSource(), 35632);
|
||||
int vertexShader = ShaderProgram.compileShader(ShaderSource.getByFileName(vertexName + ".vsh").getSource(), 35633);
|
||||
// Shader compilation can fail on some drivers (notably Intel forward-compatible
|
||||
// GL contexts, which reject legacy GLSL such as texture2D). Degrade gracefully —
|
||||
// disable this program so callers skip the effect — instead of crashing the game.
|
||||
if (fragmentShader == 0 || vertexShader == 0) {
|
||||
if (fragmentShader != 0) GL20.glDeleteShader(fragmentShader);
|
||||
if (vertexShader != 0) GL20.glDeleteShader(vertexShader);
|
||||
LOGGER.error("Shader '{}' disabled: compilation failed; the effect will be skipped.", fragmentName);
|
||||
this.modelViewUniform = null;
|
||||
this.projectionUniform = null;
|
||||
this.valid = false;
|
||||
return;
|
||||
}
|
||||
GL20.glAttachShader(this.programId, fragmentShader);
|
||||
GL20.glAttachShader(this.programId, vertexShader);
|
||||
for (Map.Entry<Integer, String> entry : attributesSupplier.get().entrySet()) {
|
||||
@@ -59,15 +73,25 @@ public class ShaderProgram {
|
||||
GL20.glLinkProgram(this.programId);
|
||||
if (GL20.glGetProgrami(this.programId, 35714) == 0) {
|
||||
LOGGER.error(GL20.glGetProgramInfoLog(this.programId, Short.MAX_VALUE));
|
||||
throw new IllegalStateException("Failed to link shader program!");
|
||||
LOGGER.error("Shader '{}' disabled: program link failed; the effect will be skipped.", fragmentName);
|
||||
GL20.glDeleteShader(fragmentShader);
|
||||
GL20.glDeleteShader(vertexShader);
|
||||
this.modelViewUniform = null;
|
||||
this.projectionUniform = null;
|
||||
this.valid = false;
|
||||
return;
|
||||
}
|
||||
GL20.glDeleteShader(fragmentShader);
|
||||
GL20.glDeleteShader(vertexShader);
|
||||
this.modelViewUniform = new Matrix4Uniform(modelViewName).bindToProgram(this.programId);
|
||||
this.projectionUniform = new Matrix4Uniform(projName).bindToProgram(this.programId);
|
||||
this.valid = true;
|
||||
}
|
||||
|
||||
public void use() {
|
||||
if (!this.valid) {
|
||||
return;
|
||||
}
|
||||
prevProgram = GL20.glGetInteger(35725);
|
||||
GL20.glUseProgram(this.programId);
|
||||
this.setModelView(RenderSystem.getModelViewMatrix());
|
||||
@@ -112,8 +136,9 @@ public class ShaderProgram {
|
||||
GL20.glShaderSource(shader, source);
|
||||
GL20.glCompileShader(shader);
|
||||
if (GL20.glGetShaderi(shader, 35713) == 0) {
|
||||
LOGGER.error(GL20.glGetShaderInfoLog(shader, Short.MAX_VALUE));
|
||||
throw new IllegalStateException(String.format("Failed to compile shader! (Type: %s)", new Object[]{type}));
|
||||
LOGGER.error("Failed to compile shader (type {}):\n{}", type, GL20.glGetShaderInfoLog(shader, Short.MAX_VALUE));
|
||||
GL20.glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
@@ -141,6 +141,11 @@ extends ClientBase {
|
||||
if (roundedRectShader == null) {
|
||||
roundedRectShader = new ShaderProgram("rounded_rect", "vertex_color", ShaderFormats.POSITION_UV_COLOR);
|
||||
}
|
||||
if (!roundedRectShader.isValid()) {
|
||||
// Rounded-rect shader unavailable on this driver — draw a plain rect instead of crashing.
|
||||
RenderUtil.drawFilledRect(poseStack, x, y, width, height, color);
|
||||
return;
|
||||
}
|
||||
Matrix4f matrix4f = poseStack.last().pose();
|
||||
roundedRectShader.use();
|
||||
GL20.glUniform2f(roundedRectShader.getUniformLocation("Size"), width, height);
|
||||
@@ -222,6 +227,11 @@ extends ClientBase {
|
||||
if (blurShader == null) {
|
||||
blurShader = new ShaderProgram("blur", ShaderFormats.POSITION_UV_COLOR);
|
||||
}
|
||||
if (!blurShader.isValid()) {
|
||||
// Blur shader unavailable on this driver — disable blur for the rest of the session.
|
||||
blurFailed = true;
|
||||
return;
|
||||
}
|
||||
if (mainRenderTarget == null) {
|
||||
mainRenderTarget = mc.getMainRenderTarget();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user