vcti-shader-foundry 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. vcti_shader_foundry-3.0.0/LICENSE +8 -0
  2. vcti_shader_foundry-3.0.0/PKG-INFO +145 -0
  3. vcti_shader_foundry-3.0.0/README.md +112 -0
  4. vcti_shader_foundry-3.0.0/pyproject.toml +93 -0
  5. vcti_shader_foundry-3.0.0/setup.cfg +4 -0
  6. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/__init__.py +111 -0
  7. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/cli.py +212 -0
  8. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/compose.py +225 -0
  9. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/__init__.py +92 -0
  10. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/archive.py +167 -0
  11. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/artifact.py +119 -0
  12. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/compile.py +140 -0
  13. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/contracts.py +169 -0
  14. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/integrity.py +272 -0
  15. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/sources.py +80 -0
  16. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/std140.py +213 -0
  17. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/structure.py +51 -0
  18. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/features.py +53 -0
  19. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/manifest.py +339 -0
  20. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/pins.py +81 -0
  21. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/py.typed +0 -0
  22. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/category_fringe.slang +76 -0
  23. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/dof6_fringe.slang +95 -0
  24. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/scalar_fringe.slang +92 -0
  25. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/symtensor_fringe.slang +97 -0
  26. vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/vector_fringe.slang +90 -0
  27. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/PKG-INFO +145 -0
  28. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/SOURCES.txt +46 -0
  29. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/dependency_links.txt +1 -0
  30. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/entry_points.txt +2 -0
  31. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/requires.txt +20 -0
  32. vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/top_level.txt +1 -0
  33. vcti_shader_foundry-3.0.0/tests/test_cli.py +260 -0
  34. vcti_shader_foundry-3.0.0/tests/test_compose.py +207 -0
  35. vcti_shader_foundry-3.0.0/tests/test_docs.py +268 -0
  36. vcti_shader_foundry-3.0.0/tests/test_emit_archive.py +189 -0
  37. vcti_shader_foundry-3.0.0/tests/test_emit_artifact.py +155 -0
  38. vcti_shader_foundry-3.0.0/tests/test_emit_compile.py +98 -0
  39. vcti_shader_foundry-3.0.0/tests/test_emit_contracts.py +169 -0
  40. vcti_shader_foundry-3.0.0/tests/test_emit_integrity.py +449 -0
  41. vcti_shader_foundry-3.0.0/tests/test_emit_sources.py +60 -0
  42. vcti_shader_foundry-3.0.0/tests/test_emit_std140.py +125 -0
  43. vcti_shader_foundry-3.0.0/tests/test_examples.py +268 -0
  44. vcti_shader_foundry-3.0.0/tests/test_features.py +77 -0
  45. vcti_shader_foundry-3.0.0/tests/test_manifest.py +266 -0
  46. vcti_shader_foundry-3.0.0/tests/test_pins.py +89 -0
  47. vcti_shader_foundry-3.0.0/tests/test_render.py +377 -0
  48. vcti_shader_foundry-3.0.0/tests/test_version.py +15 -0
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2018-2026 Visual Collaboration Technologies Inc.
2
+ All Rights Reserved.
3
+
4
+ This software is proprietary and confidential. Unauthorized copying,
5
+ distribution, or use of this software, via any medium, is strictly
6
+ prohibited. Access is granted only to authorized VCollab developers
7
+ and individuals explicitly authorized by Visual Collaboration
8
+ Technologies Inc.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: vcti-shader-foundry
3
+ Version: 3.0.0
4
+ Summary: Shader artifact-set builder: composes the installed shader features into the pipeline matrix, drives the compiler, and emits the artifact set for VCollab renderers
5
+ Author: Visual Collaboration Technologies Inc.
6
+ License-Expression: LicenseRef-Proprietary
7
+ Project-URL: Repository, https://github.com/vcollab/vcti-python-shader-foundry
8
+ Project-URL: Changelog, https://github.com/vcollab/vcti-python-shader-foundry/blob/main/CHANGELOG.md
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: <3.15,>=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: vcti-shader-compiler>=4.1.0
17
+ Requires-Dist: vcti-shader-base>=1.0.2
18
+ Requires-Dist: vcti-shader-deform>=1.1.0
19
+ Requires-Dist: vcti-shader-derive>=2.1.0
20
+ Requires-Dist: vcti-shader-fringe>=1.1.0
21
+ Requires-Dist: vcti-derived<5,>=4.0
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest; extra == "test"
24
+ Requires-Dist: pytest-cov; extra == "test"
25
+ Provides-Extra: gl
26
+ Requires-Dist: vcti-shader-compiler[gl]>=4.1.0; extra == "gl"
27
+ Requires-Dist: numpy>=1.26; extra == "gl"
28
+ Provides-Extra: lint
29
+ Requires-Dist: ruff; extra == "lint"
30
+ Provides-Extra: typecheck
31
+ Requires-Dist: mypy; extra == "typecheck"
32
+ Dynamic: license-file
33
+
34
+ # vcti-shader-foundry
35
+
36
+ The shader artifact-set **builder**: composes the installed shader features into the pipeline matrix, drives the compiler, and emits the artifact set for VCollab renderers.
37
+
38
+ ## Overview
39
+
40
+ `vcti-shader-foundry` is the assembler of the shader system — the offline build
41
+ tool that turns the installed shader features into the committed artifact set
42
+ the runtime ships. It composes the features it lists (`vcti-shader-deform`,
43
+ `-derive`, `-fringe`) into the pipeline matrix, drives `vcti-shader-compiler`
44
+ to emit and validate GLSL ES, checks every result against its contract, and
45
+ packs the shaders, one envelope per pipeline and the `registry.json` index into
46
+ a single archive, written atomically, for a consumer to take as it likes.
47
+
48
+ Composition is the whole job, and it is the part no feature can do: each
49
+ feature declares its own fields and cannot see what it is combined with, so the
50
+ builder is where the clip-space transform that belongs to nobody is added,
51
+ where a field name two features declare differently is rejected, and where the
52
+ manifest — the only contract the runtime reads — is written.
53
+
54
+ It is **not** the toolchain and **not** the vocabulary: the `.slang → GLSL ES`
55
+ machinery lives in `vcti-shader-compiler`, the specs you declare fields with in
56
+ `vcti-shader-base`, and each shader feature in its own package. See
57
+ [docs/design.md](docs/design.md) for the system design and
58
+ [docs/extending.md](docs/extending.md) for adding a shader feature.
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install vcti-shader-foundry
64
+ ```
65
+
66
+ This is a build tool, not a runtime dependency; install it in the authoring
67
+ environment alongside the feature packages you want built. The shader compilers
68
+ (`slangc`, `spirv-cross`, `glslang`) are external and pinned — discovered via
69
+ `SLANG_DIR` / `SPIRV_CROSS_DIR` / `GLSLANG_DIR` or `PATH`.
70
+
71
+ ## Quick Start
72
+
73
+ Build the artifact set as one archive:
74
+
75
+ ```console
76
+ $ shader-foundry emit
77
+ wrote shaders-3.0.0-1fc4c3a7.zip: 15 pipeline(s), 531,073 bytes, sha256 1fc4c3a7f81222b59c74378dab9c5f46610b55276b319b5d200c62b0a6df9919
78
+ registry.json, pipelines/*.json (15), shaders/*.glsl (30)
79
+ toolchain slangc 2026.12.0.1, spirv-cross vulkan-sdk-1.4.357.0, glslang 11:16.5.0 (pinned)
80
+ ```
81
+
82
+ One file, three kinds of content, because the same build is read three ways:
83
+ `registry.json` for a consumer choosing a pipeline, one self-contained envelope
84
+ per pipeline — contract, uniform layout and sources inline — for one rendering
85
+ it, and the `.glsl` files for a person. The archive is written whole or not at
86
+ all: everything is built and checked in memory, then written to a temporary
87
+ file and renamed into place. It is also reproducible, so identical inputs give
88
+ an identical file and the hash above identifies the set. The default name
89
+ carries both facts a reader wants — the version that built it and the first
90
+ eight digits of that hash — and `--out` takes any path, with `{version}` and
91
+ `{sha8}` filled in wherever they appear; a plain `shaders.zip` is a fixed name.
92
+
93
+ Give a directory instead of a `.zip` path and the same files are unpacked into
94
+ it — to read them, or to build a subset with `--pipeline ID` (repeatable) while
95
+ iterating on one shape. A subset carries no `registry.json`, because the index
96
+ is what a consumer selects from and must not list a pipeline that was not
97
+ built, and it cannot become an archive. `shader-foundry pipelines` lists the
98
+ ids, and needs no toolchain installed.
99
+
100
+ The executable versions a set is built with are pinned in the package. `emit`
101
+ warns when the installed tools differ from the pins and refuses with
102
+ `--strict-toolchain`; the manifest records what was actually used either way.
103
+
104
+ The same from Python, when a build needs to do more around it:
105
+
106
+ ```python
107
+ # requires: the shader toolchain (slangc, spirv-cross, glslang)
108
+ from vcti.shader.compiler import discover_toolchain
109
+ from vcti.shader.foundry import build_artifact_set, build_manifest, toolchain_info, write_archive
110
+
111
+ toolchain = discover_toolchain()
112
+ artifact_set = build_artifact_set(build_manifest(toolchain_info(toolchain)), toolchain)
113
+ print(write_archive(artifact_set)) # shaders-<version>-<sha8>.zip, the default name
114
+ ```
115
+
116
+ Inspect the matrix without compiling anything — 12 continuous-fringe pipelines
117
+ over four data families and three vertex variants, plus 3 discrete-fringe ones:
118
+
119
+ ```python
120
+ from vcti.shader.foundry import build_pipelines
121
+
122
+ for pipeline in build_pipelines():
123
+ print(pipeline.id, list(pipeline.capabilities))
124
+ # deform3.symtensor.fringe-float ['deform3', 'symtensor', 'fringe-float']
125
+ ```
126
+
127
+ ## Dependencies
128
+
129
+ `vcti-shader-compiler`, `vcti-shader-base`, the feature packages
130
+ (`vcti-shader-deform`, `-derive`, `-fringe`), plus `vcti-derived`. The shader
131
+ toolchain executables (`slangc`, `spirv-cross`, `glslang`) are external, not pip
132
+ dependencies. Nothing here needs a GL context: `moderngl` and `numpy` live in
133
+ the `gl` extra, for the tests that prove each emitted pair links, draws, and
134
+ computes what its file says it does; `pip install "vcti-shader-foundry[gl]"`
135
+ pulls them in on a machine that has a GL stack.
136
+
137
+ ## Documentation
138
+
139
+ | If you want to… | Read |
140
+ |---|---|
141
+ | Understand the system design and decisions | [docs/design.md](docs/design.md) |
142
+ | Add a new shader feature | [docs/extending.md](docs/extending.md) |
143
+ | See practical build workflows | [docs/patterns.md](docs/patterns.md) |
144
+ | Navigate and modify the builder source | [docs/source-guide.md](docs/source-guide.md) |
145
+ | Look up a specific function or type | [docs/api.md](docs/api.md) |
@@ -0,0 +1,112 @@
1
+ # vcti-shader-foundry
2
+
3
+ The shader artifact-set **builder**: composes the installed shader features into the pipeline matrix, drives the compiler, and emits the artifact set for VCollab renderers.
4
+
5
+ ## Overview
6
+
7
+ `vcti-shader-foundry` is the assembler of the shader system — the offline build
8
+ tool that turns the installed shader features into the committed artifact set
9
+ the runtime ships. It composes the features it lists (`vcti-shader-deform`,
10
+ `-derive`, `-fringe`) into the pipeline matrix, drives `vcti-shader-compiler`
11
+ to emit and validate GLSL ES, checks every result against its contract, and
12
+ packs the shaders, one envelope per pipeline and the `registry.json` index into
13
+ a single archive, written atomically, for a consumer to take as it likes.
14
+
15
+ Composition is the whole job, and it is the part no feature can do: each
16
+ feature declares its own fields and cannot see what it is combined with, so the
17
+ builder is where the clip-space transform that belongs to nobody is added,
18
+ where a field name two features declare differently is rejected, and where the
19
+ manifest — the only contract the runtime reads — is written.
20
+
21
+ It is **not** the toolchain and **not** the vocabulary: the `.slang → GLSL ES`
22
+ machinery lives in `vcti-shader-compiler`, the specs you declare fields with in
23
+ `vcti-shader-base`, and each shader feature in its own package. See
24
+ [docs/design.md](docs/design.md) for the system design and
25
+ [docs/extending.md](docs/extending.md) for adding a shader feature.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install vcti-shader-foundry
31
+ ```
32
+
33
+ This is a build tool, not a runtime dependency; install it in the authoring
34
+ environment alongside the feature packages you want built. The shader compilers
35
+ (`slangc`, `spirv-cross`, `glslang`) are external and pinned — discovered via
36
+ `SLANG_DIR` / `SPIRV_CROSS_DIR` / `GLSLANG_DIR` or `PATH`.
37
+
38
+ ## Quick Start
39
+
40
+ Build the artifact set as one archive:
41
+
42
+ ```console
43
+ $ shader-foundry emit
44
+ wrote shaders-3.0.0-1fc4c3a7.zip: 15 pipeline(s), 531,073 bytes, sha256 1fc4c3a7f81222b59c74378dab9c5f46610b55276b319b5d200c62b0a6df9919
45
+ registry.json, pipelines/*.json (15), shaders/*.glsl (30)
46
+ toolchain slangc 2026.12.0.1, spirv-cross vulkan-sdk-1.4.357.0, glslang 11:16.5.0 (pinned)
47
+ ```
48
+
49
+ One file, three kinds of content, because the same build is read three ways:
50
+ `registry.json` for a consumer choosing a pipeline, one self-contained envelope
51
+ per pipeline — contract, uniform layout and sources inline — for one rendering
52
+ it, and the `.glsl` files for a person. The archive is written whole or not at
53
+ all: everything is built and checked in memory, then written to a temporary
54
+ file and renamed into place. It is also reproducible, so identical inputs give
55
+ an identical file and the hash above identifies the set. The default name
56
+ carries both facts a reader wants — the version that built it and the first
57
+ eight digits of that hash — and `--out` takes any path, with `{version}` and
58
+ `{sha8}` filled in wherever they appear; a plain `shaders.zip` is a fixed name.
59
+
60
+ Give a directory instead of a `.zip` path and the same files are unpacked into
61
+ it — to read them, or to build a subset with `--pipeline ID` (repeatable) while
62
+ iterating on one shape. A subset carries no `registry.json`, because the index
63
+ is what a consumer selects from and must not list a pipeline that was not
64
+ built, and it cannot become an archive. `shader-foundry pipelines` lists the
65
+ ids, and needs no toolchain installed.
66
+
67
+ The executable versions a set is built with are pinned in the package. `emit`
68
+ warns when the installed tools differ from the pins and refuses with
69
+ `--strict-toolchain`; the manifest records what was actually used either way.
70
+
71
+ The same from Python, when a build needs to do more around it:
72
+
73
+ ```python
74
+ # requires: the shader toolchain (slangc, spirv-cross, glslang)
75
+ from vcti.shader.compiler import discover_toolchain
76
+ from vcti.shader.foundry import build_artifact_set, build_manifest, toolchain_info, write_archive
77
+
78
+ toolchain = discover_toolchain()
79
+ artifact_set = build_artifact_set(build_manifest(toolchain_info(toolchain)), toolchain)
80
+ print(write_archive(artifact_set)) # shaders-<version>-<sha8>.zip, the default name
81
+ ```
82
+
83
+ Inspect the matrix without compiling anything — 12 continuous-fringe pipelines
84
+ over four data families and three vertex variants, plus 3 discrete-fringe ones:
85
+
86
+ ```python
87
+ from vcti.shader.foundry import build_pipelines
88
+
89
+ for pipeline in build_pipelines():
90
+ print(pipeline.id, list(pipeline.capabilities))
91
+ # deform3.symtensor.fringe-float ['deform3', 'symtensor', 'fringe-float']
92
+ ```
93
+
94
+ ## Dependencies
95
+
96
+ `vcti-shader-compiler`, `vcti-shader-base`, the feature packages
97
+ (`vcti-shader-deform`, `-derive`, `-fringe`), plus `vcti-derived`. The shader
98
+ toolchain executables (`slangc`, `spirv-cross`, `glslang`) are external, not pip
99
+ dependencies. Nothing here needs a GL context: `moderngl` and `numpy` live in
100
+ the `gl` extra, for the tests that prove each emitted pair links, draws, and
101
+ computes what its file says it does; `pip install "vcti-shader-foundry[gl]"`
102
+ pulls them in on a machine that has a GL stack.
103
+
104
+ ## Documentation
105
+
106
+ | If you want to… | Read |
107
+ |---|---|
108
+ | Understand the system design and decisions | [docs/design.md](docs/design.md) |
109
+ | Add a new shader feature | [docs/extending.md](docs/extending.md) |
110
+ | See practical build workflows | [docs/patterns.md](docs/patterns.md) |
111
+ | Navigate and modify the builder source | [docs/source-guide.md](docs/source-guide.md) |
112
+ | Look up a specific function or type | [docs/api.md](docs/api.md) |
@@ -0,0 +1,93 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vcti-shader-foundry"
7
+ version = "3.0.0"
8
+ description = "Shader artifact-set builder: composes the installed shader features into the pipeline matrix, drives the compiler, and emits the artifact set for VCollab renderers"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Visual Collaboration Technologies Inc."}
12
+ ]
13
+ license = "LicenseRef-Proprietary"
14
+ license-files = ["LICENSE"]
15
+ classifiers = [
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ ]
21
+ requires-python = ">=3.12,<3.15"
22
+ dependencies = [
23
+ # The compiler, without its GL extra: the builder drives the toolchain as
24
+ # subprocesses and never opens a GL context. Only the render test does, and
25
+ # it asks for the extra itself. 4.1 is the API this builder calls:
26
+ # toolchain_versions, which fills the manifest's provenance, arrived there.
27
+ "vcti-shader-compiler>=4.1.0",
28
+ "vcti-shader-base>=1.0.2",
29
+ # The features this build composes, listed in features.py.
30
+ "vcti-shader-deform>=1.1.0",
31
+ "vcti-shader-derive>=2.1.0",
32
+ "vcti-shader-fringe>=1.1.0",
33
+ # The data family is what the pipeline matrix iterates, so the composer
34
+ # names the type even though every value it uses comes from derive's
35
+ # implemented set. Kept in step with derive's own floor.
36
+ "vcti-derived>=4.0,<5",
37
+ ]
38
+
39
+ [project.urls]
40
+ Repository = "https://github.com/vcollab/vcti-python-shader-foundry"
41
+ Changelog = "https://github.com/vcollab/vcti-python-shader-foundry/blob/main/CHANGELOG.md"
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+ include = ["vcti.shader.foundry", "vcti.shader.foundry.*"]
46
+
47
+ [tool.setuptools.package-data]
48
+ "vcti.shader.foundry" = ["py.typed", "slang/*.slang"]
49
+
50
+ [project.scripts]
51
+ shader-foundry = "vcti.shader.foundry.cli:main"
52
+
53
+ [project.optional-dependencies]
54
+ test = ["pytest", "pytest-cov"]
55
+ # The render test alone opens a GL context and builds vertex buffers; the
56
+ # builder does neither. `gl` is the fleet's name for that extra, and it is
57
+ # separate from `test` because moderngl's glcontext has no prebuilt wheel for
58
+ # every platform and Python the matrix runs, and building it needs X11 headers
59
+ # a plain runner lacks. ci.yml installs `test` and skips the render test for
60
+ # want of a toolchain anyway; ci-shader.yml installs `test,gl`.
61
+ gl = ["vcti-shader-compiler[gl]>=4.1.0", "numpy>=1.26"]
62
+ lint = ["ruff"]
63
+ typecheck = ["mypy"]
64
+
65
+ [tool.pytest.ini_options]
66
+ addopts = "--cov=vcti.shader.foundry --cov-report=term-missing --cov-fail-under=95"
67
+
68
+ [tool.mypy]
69
+ python_version = "3.12"
70
+ strict = true
71
+ files = ["src"]
72
+ namespace_packages = true
73
+ explicit_package_bases = true
74
+ mypy_path = ["src"]
75
+
76
+ [tool.coverage.run]
77
+ branch = true
78
+
79
+ [tool.coverage.report]
80
+ exclude_also = [
81
+ "raise NotImplementedError",
82
+ "if TYPE_CHECKING:",
83
+ "if __name__ == .__main__.:",
84
+ "@(abc\\.)?abstractmethod",
85
+ "\\.\\.\\.",
86
+ ]
87
+
88
+ [tool.ruff]
89
+ target-version = "py312"
90
+ line-length = 99
91
+
92
+ [tool.ruff.lint]
93
+ select = ["E", "F", "W", "I", "UP"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,111 @@
1
+ # Copyright Visual Collaboration Technologies Inc. All Rights Reserved.
2
+ # See LICENSE for details.
3
+ """vcti.shader.foundry — the shader artifact-set builder.
4
+
5
+ The builder composes the installed features into shaders, compiles them, and
6
+ writes the artifact set: the shader stages, one self-contained envelope per
7
+ pipeline, and the ``registry.json`` index a consumer selects from. Its pieces:
8
+
9
+ - :mod:`features` — the features this build composes, listed explicitly, and the
10
+ Slang search paths and capability vocabulary derived from them.
11
+ - :mod:`manifest` — the artifact-set manifest and its serialization. It lives in
12
+ the builder because the builder is the only thing that produces one, so the
13
+ document and the code that assembles it stay together.
14
+ - :mod:`compose` — the pipeline matrix: the two shapes the features combine
15
+ into, the clip transform no feature owns, and the cross-feature field-name
16
+ checks no feature can perform alone.
17
+ - :mod:`emit` — compiles each composed pipeline through the toolchain, checks
18
+ the result against its contract, and publishes the staged set: the GLSL
19
+ stages, the envelopes, and the index.
20
+ - :mod:`pins` — the toolchain versions a set is built with, and the drift
21
+ check an emit runs against them.
22
+ - :mod:`cli` — the ``shader-foundry`` command.
23
+
24
+ Nothing here opens a GL context. The builder drives the toolchain as
25
+ subprocesses and emits files; rendering a pipeline to an image is a consumer's
26
+ job, and the only GL in this repo is in the tests that prove each emitted pair
27
+ links, rasterizes and computes what its file says it does.
28
+ """
29
+
30
+ from importlib.metadata import version
31
+
32
+ from .compose import (
33
+ CLIP_TRANSFORM,
34
+ VERTEX_STAGES,
35
+ build_manifest,
36
+ build_pipelines,
37
+ category_pipeline,
38
+ derived_value_pipeline,
39
+ )
40
+ from .emit import (
41
+ ARCHIVE_NAME,
42
+ SLANG_DIR,
43
+ SOURCES,
44
+ ArtifactSet,
45
+ CompiledPipeline,
46
+ SlangSource,
47
+ archive_bytes,
48
+ archive_name,
49
+ build_artifact_set,
50
+ compile_pipeline,
51
+ contract_problems,
52
+ toolchain_info,
53
+ write_archive,
54
+ write_tree,
55
+ )
56
+ from .features import FEATURES, capability_vocabulary, features_for, include_dirs
57
+ from .manifest import (
58
+ SCHEMA_VERSION,
59
+ GeneratorInfo,
60
+ Manifest,
61
+ PipelineContract,
62
+ ToolchainInfo,
63
+ contract_sha256,
64
+ dump_envelope,
65
+ dump_manifest,
66
+ manifest_to_dict,
67
+ pipeline_envelope,
68
+ )
69
+ from .pins import PINNED_TOOLCHAIN, toolchain_drift
70
+
71
+ __version__ = version("vcti-shader-foundry")
72
+
73
+ __all__ = [
74
+ "ARCHIVE_NAME",
75
+ "CLIP_TRANSFORM",
76
+ "FEATURES",
77
+ "ArtifactSet",
78
+ "CompiledPipeline",
79
+ "GeneratorInfo",
80
+ "Manifest",
81
+ "PINNED_TOOLCHAIN",
82
+ "PipelineContract",
83
+ "SCHEMA_VERSION",
84
+ "SLANG_DIR",
85
+ "SOURCES",
86
+ "SlangSource",
87
+ "ToolchainInfo",
88
+ "VERTEX_STAGES",
89
+ "__version__",
90
+ "archive_bytes",
91
+ "archive_name",
92
+ "build_artifact_set",
93
+ "build_manifest",
94
+ "build_pipelines",
95
+ "capability_vocabulary",
96
+ "category_pipeline",
97
+ "compile_pipeline",
98
+ "contract_problems",
99
+ "contract_sha256",
100
+ "derived_value_pipeline",
101
+ "dump_envelope",
102
+ "dump_manifest",
103
+ "features_for",
104
+ "include_dirs",
105
+ "manifest_to_dict",
106
+ "pipeline_envelope",
107
+ "toolchain_drift",
108
+ "toolchain_info",
109
+ "write_archive",
110
+ "write_tree",
111
+ ]
@@ -0,0 +1,212 @@
1
+ # Copyright Visual Collaboration Technologies Inc. All Rights Reserved.
2
+ # See LICENSE for details.
3
+ """The ``shader-foundry`` command.
4
+
5
+ Emitting the artifact set is the builder's whole output, and it was reachable
6
+ only by writing a Python script around the library. That is a poor front door
7
+ for the thing every consumer of the set begins from — a CI job regenerating
8
+ artifacts, or someone building a set to see what comes out.
9
+
10
+ ``emit --out shaders.zip`` writes the set as one archive, which is the form a
11
+ consumer takes it in. ``emit --out some/dir`` unpacks the same files into a
12
+ directory, for reading them or for ``--pipeline`` subsets while iterating; an
13
+ archive is always the complete set, so the two options do not combine.
14
+
15
+ Listing needs no toolchain, which is deliberate: ``--pipeline`` takes ids, and
16
+ finding one should not require having slangc installed.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import argparse
22
+ import hashlib
23
+ import sys
24
+ from collections.abc import Sequence
25
+ from pathlib import Path
26
+
27
+ from vcti.shader.compiler import ToolchainError, discover_toolchain
28
+
29
+ from .compose import build_manifest
30
+ from .emit import (
31
+ ARCHIVE_NAME,
32
+ INDEX_FILE,
33
+ PIPELINES_DIR,
34
+ SHADERS_DIR,
35
+ SOURCES,
36
+ SlangSource,
37
+ build_artifact_set,
38
+ toolchain_info,
39
+ write_archive,
40
+ write_tree,
41
+ )
42
+ from .pins import toolchain_drift
43
+
44
+
45
+ def _build_parser() -> argparse.ArgumentParser:
46
+ parser = argparse.ArgumentParser(
47
+ prog="shader-foundry",
48
+ description="Compose and emit the shader artifact set.",
49
+ )
50
+ sub = parser.add_subparsers(dest="command", required=True)
51
+
52
+ emit = sub.add_parser("emit", help="compile the artifact set and write it out")
53
+ emit.add_argument(
54
+ "-o",
55
+ "--out",
56
+ type=Path,
57
+ default=Path(ARCHIVE_NAME),
58
+ help=(
59
+ "where to write: a path ending in .zip is the set as one archive, written "
60
+ "atomically, and may carry {version} and {sha8}, filled from the set that "
61
+ f"was built (default: {ARCHIVE_NAME}); anything else is a directory the same "
62
+ "files are unpacked into"
63
+ ),
64
+ )
65
+ emit.add_argument(
66
+ "--pipeline",
67
+ action="append",
68
+ metavar="ID",
69
+ help=(
70
+ "build only this pipeline; repeatable, and the default is all of them. "
71
+ "Only for a directory: an archive is always the complete set"
72
+ ),
73
+ )
74
+ emit.add_argument(
75
+ "--strict-toolchain",
76
+ action="store_true",
77
+ help=(
78
+ "refuse to emit unless every tool is the pinned version; without it a "
79
+ "difference is a warning and the emit proceeds"
80
+ ),
81
+ )
82
+
83
+ sub.add_parser("pipelines", help="list the pipelines this build composes")
84
+ return parser
85
+
86
+
87
+ def main(argv: Sequence[str] | None = None) -> int:
88
+ """Run the CLI; return the process exit code."""
89
+ args = _build_parser().parse_args(argv)
90
+
91
+ if args.command == "pipelines":
92
+ for pipeline_id in SOURCES:
93
+ print(pipeline_id)
94
+ return 0
95
+
96
+ return _emit(args)
97
+
98
+
99
+ def _select(requested: Sequence[str] | None) -> dict[str, SlangSource] | None:
100
+ """The sources to emit, or ``None`` if an id was not recognized.
101
+
102
+ A typo'd id is refused rather than quietly emitting nothing: a build that
103
+ reports success having written no pipeline is the kind of thing a CI job
104
+ keeps doing for weeks.
105
+ """
106
+ if not requested:
107
+ return dict(SOURCES)
108
+ unknown = sorted(set(requested) - set(SOURCES))
109
+ if unknown:
110
+ print(
111
+ f"error: no pipeline(s) {', '.join(unknown)} in this build. "
112
+ "Run 'shader-foundry pipelines' for the list.",
113
+ file=sys.stderr,
114
+ )
115
+ return None
116
+ return {pipeline_id: SOURCES[pipeline_id] for pipeline_id in requested}
117
+
118
+
119
+ def _is_archive(path: Path) -> bool:
120
+ return path.suffix.lower() == ".zip"
121
+
122
+
123
+ def _emit(args: argparse.Namespace) -> int:
124
+ out: Path = args.out
125
+ if not _is_archive(out) and ("{" in str(out) or "}" in str(out)):
126
+ print(
127
+ "error: {version} and {sha8} are filled in an archive's name; a directory "
128
+ "target takes a plain path",
129
+ file=sys.stderr,
130
+ )
131
+ return 2
132
+ if _is_archive(out) and args.pipeline:
133
+ # Refused up front: an archive is the set, and a file that looked like
134
+ # one while holding three pipelines is the artefact this rule exists to
135
+ # make impossible.
136
+ print(
137
+ "error: an archive is always the complete set; drop --pipeline, or give a "
138
+ "directory to write a partial set into",
139
+ file=sys.stderr,
140
+ )
141
+ return 2
142
+ sources = _select(args.pipeline)
143
+ if sources is None:
144
+ return 2
145
+ try:
146
+ toolchain = discover_toolchain()
147
+ except ToolchainError as error:
148
+ print(f"error: {error}", file=sys.stderr)
149
+ return 2
150
+
151
+ provenance = toolchain_info(toolchain)
152
+ drift = toolchain_drift(provenance)
153
+ for line in drift:
154
+ print(f"warning: toolchain is not the pinned one: {line}", file=sys.stderr)
155
+ if drift and args.strict_toolchain:
156
+ # Refused before anything is built: the point of --strict-toolchain is
157
+ # that a set from the wrong tools never exists to be committed.
158
+ print(
159
+ "error: --strict-toolchain given and the toolchain differs from the pins; "
160
+ "install the pinned versions or emit without --strict-toolchain",
161
+ file=sys.stderr,
162
+ )
163
+ return 2
164
+
165
+ try:
166
+ artifact_set = build_artifact_set(build_manifest(provenance), toolchain, sources=sources)
167
+ if _is_archive(out):
168
+ out = write_archive(artifact_set, out)
169
+ else:
170
+ write_tree(artifact_set, out)
171
+ except ValueError as error:
172
+ # A contract the shader does not honour, a partial set over an index:
173
+ # each is a refusal with a reason, and neither wrote anything.
174
+ print(f"error: {error}", file=sys.stderr)
175
+ return 2
176
+
177
+ count = len(artifact_set.pipeline_ids)
178
+ if _is_archive(out):
179
+ data = out.read_bytes()
180
+ print(
181
+ f"wrote {out}: {count} pipeline(s), {len(data):,} bytes, "
182
+ f"sha256 {hashlib.sha256(data).hexdigest()}"
183
+ )
184
+ print(
185
+ f" {INDEX_FILE}, {PIPELINES_DIR}/*.json ({count}), {SHADERS_DIR}/*.glsl ({count * 2})"
186
+ )
187
+ else:
188
+ print(f"wrote {count} pipeline(s) to {out}")
189
+ if artifact_set.complete:
190
+ print(f" index {INDEX_FILE}")
191
+ else:
192
+ # Said plainly, because a partial set that looked complete is the
193
+ # failure the rule exists to prevent: registry.json is what a
194
+ # consumer selects from, so only a complete set has one.
195
+ print(
196
+ f" index not written: {count} of {len(SOURCES)} pipelines; "
197
+ f"only a complete set has a {INDEX_FILE}"
198
+ )
199
+ print(f" envelopes {PIPELINES_DIR}/*.json ({count})")
200
+ print(f" shaders {SHADERS_DIR}/*.glsl ({count * 2})")
201
+ # Printed because it is the moment provenance is captured: if the emitted
202
+ # GLSL moves and no shader source did, this line is the reason.
203
+ print(
204
+ f" toolchain slangc {provenance.slangc}, "
205
+ f"spirv-cross {provenance.spirv_cross}, glslang {provenance.glslang}"
206
+ + (" (differs from the pins; see warnings)" if drift else " (pinned)")
207
+ )
208
+ return 0
209
+
210
+
211
+ if __name__ == "__main__": # pragma: no cover
212
+ raise SystemExit(main())