Report errors compiling custom shaders in the UI

This commit is contained in:
Kovid Goyal 2026-09-22 15:37:35 +05:30
parent a908167e98
commit 090f90dc4a
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 39 additions and 5 deletions

View file

@ -2782,6 +2782,12 @@ class Boss:
if w is not None and tab is not None:
tab.new_special_window(self.create_special_window_for_show_error(title, msg, w.id), copy_colors_from=w)
def show_custom_shader_errors(self) -> None:
errors = load_shader_programs.custom_shader_errors
if errors:
load_shader_programs.custom_shader_errors = []
self.show_error(_('Failed to load custom shaders'), '\n\n'.join(errors))
@ac('mk', 'Create a new marker')
def create_marker(self) -> None:
w = self.window_for_dispatch or self.active_window
@ -3499,6 +3505,7 @@ class Boss:
w.report_color_scheme_preference_if_wanted()
w.refresh(reload_all_gpu_data=True)
load_shader_programs.recompile_if_needed()
self.show_custom_shader_errors()
@ac(
'misc',

View file

@ -346,6 +346,9 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = (),
if bad_lines or boss.misc_config_errors:
boss.show_bad_config_lines(bad_lines, boss.misc_config_errors)
boss.misc_config_errors = []
# load_all_shaders() runs as a create_os_window() callback, before the
# Boss exists, so any custom shader failures are reported only now.
boss.show_custom_shader_errors()
if startup_session_error:
boss.show_error(
_('The startup session was invalid'),

View file

@ -191,6 +191,10 @@ class LoadShaderPrograms:
# Per-instance because it is mutable: the sources most recently compiled
# into each program, used to skip redundant recompiles.
self.last_built_custom_shaders: dict[int, tuple[str, str, dict[str, Any]]] = {}
# Errors from the most recent attempt to build the shaders named by the
# custom_shaders option. A failure here disables the shader silently as
# far as rendering is concerned, so the Boss displays these to the user.
self.custom_shader_errors: list[str] = []
def get_options(self) -> Options:
try:
@ -254,6 +258,15 @@ class LoadShaderPrograms:
self.force_recompile_of_custom_shaders = False
opts = self.get_options()
self.custom_shaders = tuple(opts.custom_shaders)
self.custom_shader_errors = []
def err(msg: str) -> None:
# Record as well as log: a failure here leaves the shader disabled
# with no visible difference from not setting custom_shaders at all,
# so the Boss shows these to the user.
log_error(msg)
self.custom_shader_errors.append(msg)
pmap: dict[str, list[Pipeline]] = {}
for k in self.custom_shaders:
try:
@ -262,15 +275,15 @@ class LoadShaderPrograms:
try:
custom_shader(k)
except Exception as e:
log_error(f'Failed to read custom shader pipeline definition from {k} with error: {e}')
err(f'Failed to read custom shader pipeline definition from {k} with error: {e}')
continue
try:
d = parse_pipeline_definition(['startgroup', f' shaders {k}', 'endgroup'], k)
except Exception as e:
log_error(f'Failed to build minimal shader pipeline for {k} with error: {e}')
err(f'Failed to build minimal shader pipeline for {k} with error: {e}')
continue
except Exception as e:
log_error(f'Failed to read custom shader pipeline definition from {k} with error: {e}')
err(f'Failed to read custom shader pipeline definition from {k} with error: {e}')
continue
pmap.setdefault(d['slot'], []).append(d)
@ -291,8 +304,19 @@ class LoadShaderPrograms:
vert, frag, metadata = build_custom_shader_pipeline_glsl(pipeline)
# print(vert, file=open('/tmp/sample.vert', 'w'))
# print(frag, file=open('/tmp/sample.frag', 'w'))
except FileNotFoundError as e:
if e.filename == slangc()[0]:
# Without slangc no custom shader can be built at all, and
# a bare "No such file or directory" gives no hint as to why.
err(
f'Failed to build custom shader for slot {slot} because the slang shader compiler'
f' ({slangc()[0]}) was not found. Install shader-slang to use custom shaders.'
)
else:
err(f'Failed to build custom shader for slot {slot} with error: {e}')
disable(prog)
except Exception as e:
log_error(f'Failed to build custom shader for slot {slot} with error: {e}')
err(f'Failed to build custom shader for slot {slot} with error: {e}')
disable(prog)
else:
try:
@ -300,7 +324,7 @@ class LoadShaderPrograms:
compile_program(prog, (vert,), (frag,), metadata, allow_recompile)
self.last_built_custom_shaders[prog] = vert, frag, metadata
except Exception as e:
log_error(f'Failed to load custom shader for slot {slot} with error: {e}')
err(f'Failed to load custom shader for slot {slot} with error: {e}')
disable(prog)
do(CUSTOM_END_PROGRAM, 'end')