Generate glsl uniforms header from slang code

This commit is contained in:
Kovid Goyal 2026-07-06 15:52:47 +05:30
parent a7c226d957
commit 9469d268fd
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 268 additions and 28 deletions

1
.gitattributes vendored
View file

@ -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

View file

@ -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);

View file

@ -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);

164
kitty/glsl-uniforms.h generated Normal file
View file

@ -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");
}

View file

@ -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<GammaLUT> glt;
#define gamma_lut glt.gamma_lut
struct Colors {
uniform uint colors[9];
};
ConstantBuffer<Colors> clrs;
#define colors clrs.colors
// Vertex attributes matching GLSL input locations
struct VertexInput {

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2026, Kovid Goyal <kovid at kovidgoyal.net>
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)