mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-08-04 14:46:03 +00:00
More work on custom shaders
This commit is contained in:
parent
81e711e9b9
commit
c0413e266b
2 changed files with 88 additions and 29 deletions
|
|
@ -5,8 +5,8 @@
|
|||
// IMPORTS
|
||||
|
||||
public struct BlitOutput {
|
||||
public float2 texcoord;
|
||||
public float2 position;
|
||||
float2 texcoord;
|
||||
float2 position;
|
||||
};
|
||||
|
||||
#define left 0
|
||||
|
|
@ -22,7 +22,7 @@ static const int2 vertex_pos_map[4] = {
|
|||
{left, top}
|
||||
};
|
||||
|
||||
public BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) {
|
||||
BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 dest_rect) {
|
||||
int2 pos = vertex_pos_map[vertex_id];
|
||||
BlitOutput output;
|
||||
output.texcoord = float2(src_rect[pos.x], src_rect[pos.y]);
|
||||
|
|
@ -30,22 +30,14 @@ public BlitOutput get_coords_for_blit(uint vertex_id, float4 src_rect, float4 de
|
|||
return output;
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
float2 texcoord : TEXCOORD;
|
||||
float4 position : SV_Position;
|
||||
};
|
||||
|
||||
|
||||
[shader("vertex")]
|
||||
VertexOutput vertex_main(float4 src_rect, float4 dest_rect, uint vertex_id : SV_VertexID) {
|
||||
public float4 pipeline_vertex_main(float4 src_rect, float4 dest_rect, uint vertex_id) {
|
||||
BlitOutput ans = get_coords_for_blit(vertex_id, src_rect, dest_rect);
|
||||
return {ans.texcoord, float4(ans.position[0], ans.position[1], 0.0, 1.0)};
|
||||
return float4(ans.texcoord[0], ans.texcoord[1], ans.position[0], ans.position[1]);
|
||||
}
|
||||
|
||||
uniform Sampler2D backbuffer;
|
||||
|
||||
[shader("fragment")]
|
||||
float4 pipeline_fragment_main(float2 texcoord : TEXCOORD) : SV_Target {
|
||||
public float4 pipeline_fragment_main(float2 texcoord) {
|
||||
KittyCustomShaderData d;
|
||||
d.backbuffer = backbuffer;
|
||||
d.texcoord = texcoord;
|
||||
|
|
|
|||
|
|
@ -540,8 +540,10 @@ def commands_to_compile_to_spirv(sources: dict[str, SlangFile], build_dir: str,
|
|||
|
||||
|
||||
# GLSL {{{
|
||||
glsl_version = max(150, GLSL_VERSION) # slangc fails with glsl_140 https://github.com/shader-slang/slang/issues/11898
|
||||
|
||||
|
||||
def commands_to_compile_to_glsl(sources: dict[str, SlangFile], build_dir: str, dest_dir: str, built_glsl_files: list[str]) -> Iterator[Command]:
|
||||
glsl_version = max(150, GLSL_VERSION) # slangc fails with glsl_140 https://github.com/shader-slang/slang/issues/11898
|
||||
for base_dest, base_build, slang_module, cmd, sfile in iter_entry_point_shaders(sources, build_dir, dest_dir):
|
||||
module_mtime = os.path.getmtime(slang_module)
|
||||
extra_cmd = ['-line-directive-mode', 'none', '-target', 'glsl', '-profile', f'glsl_{glsl_version}']
|
||||
|
|
@ -778,21 +780,25 @@ def write_if_changed(dest: str, text: str) -> None:
|
|||
f.write(text)
|
||||
|
||||
|
||||
def glsl_metadata_for_shader(path: str) -> dict[str, Any]:
|
||||
with open(path) as f:
|
||||
d = json.load(f)
|
||||
m = GLSLMetadata.fromdict(d)
|
||||
return {
|
||||
'loose_uniforms': m.loose_uniforms,
|
||||
'uniform_structs': m.uniform_structs,
|
||||
'input_locations': m.input_locations,
|
||||
'uniform_struct_names': m.uniform_struct_names,
|
||||
}
|
||||
|
||||
|
||||
def write_glsl_metadata(dest_dir: str, dest: str = 'glsl-uniforms.json') -> None:
|
||||
metadata_map = {}
|
||||
for x in glob.glob(os.path.join(dest_dir, '*.glsl.json')):
|
||||
shader_name = shader_name_from_path(x)
|
||||
if '.' in shader_name:
|
||||
continue
|
||||
with open(x) as f:
|
||||
d = json.load(f)
|
||||
m = GLSLMetadata.fromdict(d)
|
||||
metadata_map[shader_name] = {
|
||||
'loose_uniforms': m.loose_uniforms,
|
||||
'uniform_structs': m.uniform_structs,
|
||||
'input_locations': m.input_locations,
|
||||
'uniform_struct_names': m.uniform_struct_names,
|
||||
}
|
||||
metadata_map[shader_name] = glsl_metadata_for_shader(x)
|
||||
write_if_changed(os.path.join(dest_dir, dest), json.dumps(metadata_map, indent=2, sort_keys=True))
|
||||
|
||||
|
||||
|
|
@ -921,7 +927,7 @@ def custom_shader(name: str = '') -> tuple[str, bytes, bytes]:
|
|||
return name, src, key(src)
|
||||
|
||||
|
||||
def _build_custom_shader_pipeline_ir(slot: str, shaders: Iterable[str], cache_dir: str) -> str:
|
||||
def build_custom_shader_pipeline_ir(slot: str, shaders: Iterable[str], cache_dir: str) -> tuple[str, str, str]:
|
||||
slot_module_name = f'{slot.replace("-", "_")}'
|
||||
cache_dir = os.path.join(cache_dir, 'c')
|
||||
ensure_cache_dir(cache_dir)
|
||||
|
|
@ -1001,7 +1007,7 @@ def _build_custom_shader_pipeline_ir(slot: str, shaders: Iterable[str], cache_di
|
|||
cache_ok = f.read() == slot_key
|
||||
ans = os.path.join(slot_dir, f'{slot}.slang-module')
|
||||
if cache_ok:
|
||||
return ans
|
||||
return ans, libdir, slot_dir
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
for wrapper_name, wrapper_src in wrappers.items():
|
||||
with open(os.path.join(tdir, wrapper_name), 'w') as f:
|
||||
|
|
@ -1017,10 +1023,13 @@ def _build_custom_shader_pipeline_ir(slot: str, shaders: Iterable[str], cache_di
|
|||
|
||||
with open(j(f'{slot}.key'), 'wb') as f:
|
||||
f.write(slot_key)
|
||||
return ans
|
||||
return ans, libdir, slot_dir
|
||||
|
||||
|
||||
def build_custom_shader_pipeline_ir(slot: str = 'after-window-background', shaders: Iterable[str] = ('sample',), cache_dir: str = '') -> str:
|
||||
@lru_cache(maxsize=64)
|
||||
def build_custom_shader_pipeline_glsl(
|
||||
slot: str = 'after-window-background', shaders: tuple[str, ...] = ('sample',), cache_dir: str = ''
|
||||
) -> tuple[str, str, dict[str, Any]]:
|
||||
import kitty.constants as kc
|
||||
|
||||
cache_dir = os.path.join(cache_dir or kc.cache_dir(), 'shaders')
|
||||
|
|
@ -1029,7 +1038,65 @@ def build_custom_shader_pipeline_ir(slot: str = 'after-window-background', shade
|
|||
with lock_with_file(
|
||||
os.path.join(cache_dir, 'lock'),
|
||||
):
|
||||
return _build_custom_shader_pipeline_ir(slot, shaders, cache_dir)
|
||||
slang_module_path, libdir, slotsdir = build_custom_shader_pipeline_ir(slot, shaders, cache_dir)
|
||||
glsl_dir = os.path.join(os.path.dirname(os.path.dirname(slang_module_path)), 'glsl')
|
||||
os.makedirs(glsl_dir, exist_ok=True)
|
||||
module_mtime = safe_mtime(slang_module_path)
|
||||
vertex = os.path.join(glsl_dir, f'{slot}.vert.glsl')
|
||||
fragment = os.path.join(glsl_dir, f'{slot}.frag.glsl')
|
||||
metadata = os.path.join(glsl_dir, f'{slot}.glsl.json')
|
||||
if module_mtime > safe_mtime(metadata):
|
||||
cmd = list(slangc()) + [
|
||||
'-warnings-as-errors',
|
||||
'all',
|
||||
'-lang',
|
||||
'slang',
|
||||
'-I',
|
||||
slotsdir,
|
||||
'-I',
|
||||
libdir,
|
||||
'-target',
|
||||
'glsl',
|
||||
'-profile',
|
||||
f'glsl_{glsl_version}',
|
||||
]
|
||||
vcmd = cmd + ['-stage', 'vertex', '-entry', 'vmain_wrap', '-o', vertex, '--', '-']
|
||||
fcmd = cmd + ['-stage', 'fragment', '-entry', 'fmain_wrap', '-o', fragment, '--', '-']
|
||||
src = textwrap.dedent(
|
||||
"""
|
||||
#language slang 2026
|
||||
import MODULE;
|
||||
|
||||
struct VertexOutput {
|
||||
float2 texcoord : TEXCOORD;
|
||||
float4 position : SV_Position;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
VertexOutput vmain_wrap(float4 src_rect, float4 dest_rect, uint vertex_id : SV_VertexID) {
|
||||
float4 c = pipeline_vertex_main(src_rect, dest_rect, vertex_id);
|
||||
return {float2(c[0], c[1]), float4(c[2], c[3], 0, 1)};
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 fmain_wrap(float2 texcoord : TEXCOORD) : SV_Target {
|
||||
return pipeline_fragment_main(texcoord);
|
||||
}
|
||||
""".replace('MODULE', slot.replace('-', '_'))
|
||||
).encode()
|
||||
v = subprocess.Popen(vcmd, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
f = subprocess.Popen(fcmd, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
assert v.stdin is not None and f.stdin is not None
|
||||
assert v.stderr is not None and f.stderr is not None
|
||||
v.stdin.write(src), v.stdin.close()
|
||||
f.stdin.write(src), f.stdin.close()
|
||||
if (rc := v.wait()) != 0:
|
||||
raise SlangFailed(f'{slot}.vert.glsl', subprocess.CompletedProcess(vcmd, rc, stderr=v.stderr.read()))
|
||||
if (rc := f.wait()) != 0:
|
||||
raise SlangFailed(f'{slot}.frag.glsl', subprocess.CompletedProcess(fcmd, rc, stderr=f.stderr.read()))
|
||||
fixup_opengl_files((fragment, vertex))
|
||||
with open(vertex) as vf, open(fragment) as ff:
|
||||
return vf.read(), ff.read(), glsl_metadata_for_shader(metadata)
|
||||
|
||||
|
||||
def test_slang_build() -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue