diff --git a/.gitattributes b/.gitattributes index d2706257f..8efb2b5b5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,6 +11,7 @@ kitty/rgb.py linguist-generated=true kitty/srgb_gamma.* linguist-generated=true kitty/gl-wrapper.* linguist-generated=true kitty/glfw-wrapper.* linguist-generated=true +kitty/glsl-uniforms.h linguist-generated=true kitty/color-names.h linguist-generated=true kitty/parse-graphics-command.h linguist-generated=true kitty/parse-multicell-command.h linguist-generated=true diff --git a/kitty/gl.c b/kitty/gl.c index b350071e8..905813db4 100644 --- a/kitty/gl.c +++ b/kitty/gl.c @@ -299,6 +299,20 @@ get_uniform_information(int program, const char *name, GLenum information_type) return q; } +ArrayInformation +get_uniform_array_information(int program, const char *name) { + ArrayInformation ans; + const char* names[] = {""}; + names[0] = name; + GLuint pid = program_id(program); + GLuint t; + glGetUniformIndices(pid, 1, (void*)names, &t); + glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_ARRAY_STRIDE, &ans.stride); + glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_OFFSET, &ans.offset); + glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_SIZE, &ans.size); + return ans; +} + GLint attrib_location(int program, const char *name) { GLint ans = glGetAttribLocation(programs[program].id, name); diff --git a/kitty/gl.h b/kitty/gl.h index 809c78a31..f643e8092 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -14,7 +14,9 @@ typedef struct { } UniformBlock; typedef struct { - GLint offset, stride, size; + GLint offset; // byte offset from start of UBO + GLint stride; // element stride in bytes + GLint size; // umber of elements } ArrayInformation; typedef struct { @@ -47,6 +49,7 @@ GLuint block_index(int program, const char *name); GLint block_size(int program, GLuint block_index); GLint get_uniform_location(int program, const char *name); GLint get_uniform_information(int program, const char *name, GLenum information_type); +ArrayInformation get_uniform_array_information(int program, const char *name); GLint attrib_location(int program, const char *name); ssize_t create_vao(void); size_t add_buffer_to_vao(ssize_t vao_idx, GLenum usage); diff --git a/kitty/glsl-uniforms.h b/kitty/glsl-uniforms.h new file mode 100644 index 000000000..e34f6e15b --- /dev/null +++ b/kitty/glsl-uniforms.h @@ -0,0 +1,164 @@ +// generated by slang.py DO NOT EDIT +#include "gl.h" + + +typedef struct BgimageUniforms { + int background; + int image; + int positions; + int sizes; + int tiled; +} BgimageUniforms; + +static inline void get_uniform_locations_bgimage(int program, BgimageUniforms *ans) { + ans->background = get_uniform_location(program, "background_0"); + ans->image = get_uniform_location(program, "image_0"); + ans->positions = get_uniform_location(program, "positions_0"); + ans->sizes = get_uniform_location(program, "sizes_0"); + ans->tiled = get_uniform_location(program, "tiled_0"); +} + +typedef struct BlitUniforms { + int dest_rect; + int image; + int src_rect; +} BlitUniforms; + +static inline void get_uniform_locations_blit(int program, BlitUniforms *ans) { + ans->dest_rect = get_uniform_location(program, "dest_rect_0"); + ans->image = get_uniform_location(program, "image_0"); + ans->src_rect = get_uniform_location(program, "src_rect_0"); +} + +typedef struct BorderUniforms { + int background_opacity; + // Vertex Input locations + int rect; + int rect_color; + // Uniform block locations + UniformBlock Colors; + ArrayInformation colors; + UniformBlock GammaLUT; + ArrayInformation gamma_lut; +} BorderUniforms; + +static inline void get_uniform_locations_border(int program, BorderUniforms *ans) { + ans->background_opacity = get_uniform_location(program, "background_opacity_0"); + ans->rect = 0; + ans->rect_color = 1; + ans->Colors.index = block_index(program, "block_Colors_0"); + ans->Colors.size = block_size(program, ans->Colors.index); + ans->colors = get_uniform_array_information(program, "block_Colors_0.colors_0[0]"); + ans->GammaLUT.index = block_index(program, "block_GammaLUT_0"); + ans->GammaLUT.size = block_size(program, ans->GammaLUT.index); + ans->gamma_lut = get_uniform_array_information(program, "block_GammaLUT_0.gamma_lut_0[0]"); +} + +typedef struct CellUniforms { + int draw_bg_bitfield; + int sprites; + int text_contrast; + int text_gamma_adjustment; + // Vertex Input locations + int colors; + int is_selected; + int sprite_idx; + // Uniform block locations + UniformBlock CellRenderData; + UniformBlock ColorTable; + ArrayInformation color_table; + UniformBlock GammaLUT; + ArrayInformation gamma_lut; +} CellUniforms; + +static inline void get_uniform_locations_cell(int program, CellUniforms *ans) { + ans->draw_bg_bitfield = get_uniform_location(program, "draw_bg_bitfield_0"); + ans->sprites = get_uniform_location(program, "sprites_0"); + ans->text_contrast = get_uniform_location(program, "text_contrast_0"); + ans->text_gamma_adjustment = get_uniform_location(program, "text_gamma_adjustment_0"); + ans->colors = 0; + ans->is_selected = 2; + ans->sprite_idx = 1; + ans->CellRenderData.index = block_index(program, "block_CellRenderData_0"); + ans->CellRenderData.size = block_size(program, ans->CellRenderData.index); + ans->ColorTable.index = block_index(program, "block_ColorTable_0"); + ans->ColorTable.size = block_size(program, ans->ColorTable.index); + ans->color_table = get_uniform_array_information(program, "block_ColorTable_0.color_table_0[0]"); + ans->GammaLUT.index = block_index(program, "block_GammaLUT_0"); + ans->GammaLUT.size = block_size(program, ans->GammaLUT.index); + ans->gamma_lut = get_uniform_array_information(program, "block_GammaLUT_0.gamma_lut_0[0]"); +} + +typedef struct GraphicsUniforms { + int amask_bg_premult; + int amask_fg; + int dest_rect; + int extra_alpha; + int image; + int src_rect; +} GraphicsUniforms; + +static inline void get_uniform_locations_graphics(int program, GraphicsUniforms *ans) { + ans->amask_bg_premult = get_uniform_location(program, "amask_bg_premult_0"); + ans->amask_fg = get_uniform_location(program, "amask_fg_0"); + ans->dest_rect = get_uniform_location(program, "dest_rect_0"); + ans->extra_alpha = get_uniform_location(program, "extra_alpha_0"); + ans->image = get_uniform_location(program, "image_0"); + ans->src_rect = get_uniform_location(program, "src_rect_0"); +} + +typedef struct Rounded_rectUniforms { + int background_color; + int color; + int params; + int rect; +} Rounded_rectUniforms; + +static inline void get_uniform_locations_rounded_rect(int program, Rounded_rectUniforms *ans) { + ans->background_color = get_uniform_location(program, "background_color_0"); + ans->color = get_uniform_location(program, "color_0"); + ans->params = get_uniform_location(program, "params_0"); + ans->rect = get_uniform_location(program, "rect_0"); +} + +typedef struct ScreenshotUniforms { + int dest_rect; + int image; + int src_rect; + int src_size; +} ScreenshotUniforms; + +static inline void get_uniform_locations_screenshot(int program, ScreenshotUniforms *ans) { + ans->dest_rect = get_uniform_location(program, "dest_rect_0"); + ans->image = get_uniform_location(program, "image_0"); + ans->src_rect = get_uniform_location(program, "src_rect_0"); + ans->src_size = get_uniform_location(program, "src_size_0"); +} + +typedef struct TintUniforms { + int edges; + int tint_color; +} TintUniforms; + +static inline void get_uniform_locations_tint(int program, TintUniforms *ans) { + ans->edges = get_uniform_location(program, "edges_0"); + ans->tint_color = get_uniform_location(program, "tint_color_0"); +} + +typedef struct TrailUniforms { + int cursor_edge_x; + int cursor_edge_y; + int trail_color; + int trail_opacity; + int x_coords; + int y_coords; +} TrailUniforms; + +static inline void get_uniform_locations_trail(int program, TrailUniforms *ans) { + ans->cursor_edge_x = get_uniform_location(program, "cursor_edge_x_0"); + ans->cursor_edge_y = get_uniform_location(program, "cursor_edge_y_0"); + ans->trail_color = get_uniform_location(program, "trail_color_0"); + ans->trail_opacity = get_uniform_location(program, "trail_opacity_0"); + ans->x_coords = get_uniform_location(program, "x_coords_0"); + ans->y_coords = get_uniform_location(program, "y_coords_0"); +} \ No newline at end of file diff --git a/kitty/shaders/border.slang b/kitty/shaders/border.slang index cfa0dde94..bfe96a27d 100644 --- a/kitty/shaders/border.slang +++ b/kitty/shaders/border.slang @@ -15,8 +15,17 @@ import utils; #define TAB_BAR_EDGE_LEFT_COLOR 7 #define TAB_BAR_EDGE_RIGHT_COLOR 8 -uniform float gamma_lut[256]; -uniform uint colors[9]; +struct GammaLUT { + uniform float gamma_lut[256]; +}; +ConstantBuffer glt; +#define gamma_lut glt.gamma_lut + +struct Colors { + uniform uint colors[9]; +}; +ConstantBuffer clrs; +#define colors clrs.colors // Vertex attributes matching GLSL input locations struct VertexInput { diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index cf0fb8a61..5613ee7eb 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2026, Kovid Goyal +import glob import hashlib import json import os @@ -430,6 +431,12 @@ class GLSLMetadata: self.input_locations = {} self.uniform_struct_names = {} + def merge(self, other: 'GLSLMetadata') -> None: + self.loose_uniforms.update(other.loose_uniforms) + self.uniform_structs.update(other.uniform_structs) + self.uniform_struct_names.update(other.uniform_struct_names) + self.input_locations.update(other.input_locations) + def asdict(self) -> dict[str, Any]: return { 'loose_uniforms': self.loose_uniforms, @@ -458,7 +465,9 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]: uniform_name = name.rpartition('_')[0] if uniform_name in uniform_names: raise KeyError(f'The uniform name {uniform_name} is used with multiple suffixes in {path}') - uniform_names[uniform_name] = name.partition('[')[0] # ] + if '[' in name: + name = name.partition('[')[0] + '[0]' + uniform_names[uniform_name] = name return name src_lines = glsl_code.splitlines() @@ -540,32 +549,72 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]: def fixup_opengl_files(paths: Iterable[str], metadata_map: dict[str, GLSLMetadata]) -> None: ' Convert the GLSL output of slangc to something that will work with OpenGL 3.1 ' for path in paths: - with open(path, 'r+') as f: + with open(path, 'r') as f: glsl_code = f.read() - try: - fixed, metadata = fixup_opengl_code(glsl_code, path) - except Exception: - os.unlink(path) - raise - parts = os.path.basename(path).split('.') - if len(parts) == 3: - name = parts[0] - if name in metadata_map: - metadata_map[name].loose_uniforms.update(metadata.loose_uniforms) - metadata_map[name].uniform_structs.update(metadata.uniform_structs) - else: - metadata_map[name] = metadata - f.seek(0) - f.truncate() - f.write(fixed) + try: + fixed, metadata = fixup_opengl_code(glsl_code, path) + except Exception: + os.unlink(path) + raise + parts = os.path.basename(path).split('.') + write_if_changed(path, fixed) + if len(parts) == 3: + name = parts[0] + if name in metadata_map: + metadata_map[name].merge(metadata) + else: + metadata_map[name] = metadata -def write_glsl_header(metadata_map: dict[str, GLSLMetadata]) -> None: - from pprint import pprint - for name, m in metadata_map.items(): - print(name) - pprint(m.asdict()) - print() +def write_if_changed(dest: str, text: str) -> None: + with suppress(FileNotFoundError), open(dest) as f: + existing = f.read() + if existing == text: + return + with open(dest, 'w') as f: + f.write(text) + + +def write_glsl_header(metadata_map: dict[str, GLSLMetadata], dest: str = 'kitty/glsl-uniforms.h') -> None: + lines = ['// generated by slang.py DO NOT EDIT', '#include "gl.h"', ''] + a = lines.append + for name in sorted(metadata_map): + m = metadata_map[name] + struct_name = name.capitalize() + 'Uniforms' + a('') + a(f'typedef struct {struct_name} {{') # }} + for u in sorted(m.loose_uniforms): + a(f' int {u};') + if m.input_locations: + a(' // Vertex Input locations') + for u in sorted(m.input_locations): + a(f' int {u};') + if m.uniform_structs: + a(' // Uniform block locations') + for u in sorted(m.uniform_structs): + a(f' UniformBlock {u};') + for v in sorted(m.uniform_structs[u]): + vv = m.uniform_structs[u][v] + if ']' in vv: + a(f' ArrayInformation {v};') + + a(f'}} {struct_name};') + a('') + a(f'static inline void get_uniform_locations_{name}(int program, {struct_name} *ans) {{') # }} + for u in sorted(m.loose_uniforms): + a(f' ans->{u} = get_uniform_location(program, "{m.loose_uniforms[u]}");') + for u in sorted(m.input_locations): + a(f' ans->{u} = {m.input_locations[u]};') + for u in sorted(m.uniform_structs): + a(f' ans->{u}.index = block_index(program, "{m.uniform_struct_names[u]}");') + a(f' ans->{u}.size = block_size(program, ans->{u}.index);') + block_name = m.uniform_struct_names[u] + for v in sorted(m.uniform_structs[u]): + vv = m.uniform_structs[u][v] + if ']' in vv: + a(f' ans->{v} = get_uniform_array_information(program, "{block_name}.{vv}");') + a('}') + write_if_changed(dest, '\n'.join(lines)) # }}} @@ -639,7 +688,7 @@ def compile_builtin_shaders(build_dir: str, dest_dir: str, parallel_run: Paralle # Now run all commands parallel_run(chain(spirv_commands, glsl_commands)) metadata_map = {} - fixup_opengl_files(built_glsl_files, metadata_map=metadata_map) + fixup_opengl_files(glob.glob(os.path.join(dest_dir, '*.glsl')), metadata_map=metadata_map) if shutil.which('glslangValidator'): from kitty.shaders.validate_shaders import validation_command_for_file parallel_run((True, f'Validating |{os.path.basename(x)}| ...', validation_command_for_file(x)) for x in built_glsl_files)