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.
- vcti_shader_foundry-3.0.0/LICENSE +8 -0
- vcti_shader_foundry-3.0.0/PKG-INFO +145 -0
- vcti_shader_foundry-3.0.0/README.md +112 -0
- vcti_shader_foundry-3.0.0/pyproject.toml +93 -0
- vcti_shader_foundry-3.0.0/setup.cfg +4 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/__init__.py +111 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/cli.py +212 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/compose.py +225 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/__init__.py +92 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/archive.py +167 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/artifact.py +119 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/compile.py +140 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/contracts.py +169 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/integrity.py +272 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/sources.py +80 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/std140.py +213 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/emit/structure.py +51 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/features.py +53 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/manifest.py +339 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/pins.py +81 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/py.typed +0 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/category_fringe.slang +76 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/dof6_fringe.slang +95 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/scalar_fringe.slang +92 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/symtensor_fringe.slang +97 -0
- vcti_shader_foundry-3.0.0/src/vcti/shader/foundry/slang/vector_fringe.slang +90 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/PKG-INFO +145 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/SOURCES.txt +46 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/dependency_links.txt +1 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/entry_points.txt +2 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/requires.txt +20 -0
- vcti_shader_foundry-3.0.0/src/vcti_shader_foundry.egg-info/top_level.txt +1 -0
- vcti_shader_foundry-3.0.0/tests/test_cli.py +260 -0
- vcti_shader_foundry-3.0.0/tests/test_compose.py +207 -0
- vcti_shader_foundry-3.0.0/tests/test_docs.py +268 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_archive.py +189 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_artifact.py +155 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_compile.py +98 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_contracts.py +169 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_integrity.py +449 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_sources.py +60 -0
- vcti_shader_foundry-3.0.0/tests/test_emit_std140.py +125 -0
- vcti_shader_foundry-3.0.0/tests/test_examples.py +268 -0
- vcti_shader_foundry-3.0.0/tests/test_features.py +77 -0
- vcti_shader_foundry-3.0.0/tests/test_manifest.py +266 -0
- vcti_shader_foundry-3.0.0/tests/test_pins.py +89 -0
- vcti_shader_foundry-3.0.0/tests/test_render.py +377 -0
- 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,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())
|