Contributor guide¶
Start with a focused issue or pull request. Documentation fixes, reproducible bug reports, effect implementations, and platform testing all help. See the repository's contribution guidelines.
Local setup¶
- Fork and clone ShaderX.
- Install JDK 21 and configure your IDE's Gradle JDK or
JAVA_HOMEto use it. Runjava -versionand./gradlew --versionto check. - Install the Android SDK platform matching
android-compileSdkingradle/libs.versions.toml(currently API 37). SetANDROID_HOMEor addsdk.dir=/absolute/path/to/Android/sdkto the ignored rootlocal.properties. - Use the checked-in Gradle wrapper. The version catalog and wrapper are the sources of truth for dependency and build versions.
- For iOS builds, use macOS with Xcode, its command-line tools, and the required simulator installed. Linux and Windows contributors can work on JVM, Android, Wasm, and documentation without running iOS tests.
No Maven publishing credentials or TMDB token are needed for library tests or ShaderLab. The carousel optionally uses its own token. On Windows, replace ./gradlew with gradlew.bat.
Build and test¶
# Common tests and Skia integration tests on the JVM
./gradlew :shaderx:jvmTest
# Common tests in headless Chrome (install Chrome first)
./gradlew :shaderx:wasmJsBrowserTest
# A focused test class
./gradlew :shaderx:jvmTest --tests 'com.debanshu.shaderlab.shaderx.FactorySmokeTest'
# Full library build, including checks (macOS for all Apple targets)
./gradlew :shaderx:build
# Generate the API reference
./gradlew :shaderx:generateDocs
JVM test results are in shaderx/build/reports/tests/jvmTest/index.html. Dokka output is in shaderx/build/dokka/html/index.html. Use the sample commands to test visual output on the affected platforms; unit tests cannot establish how an effect looks on every GPU.
Browser tests use a Mocha setup hook to await Skia's WebAssembly runtime before executing the common tests that allocate ImageBitmaps. This avoids a startup race on slower runners; it does not skip or retry failing assertions. Browser results are in shaderx/build/reports/tests/wasmJsBrowserTest/index.html. CI runs :shaderx:build (which already includes check) and uploads XML/HTML test reports for each runner even on failure.
Release numbering and branch guards have Python regression tests alongside the documentation-policy tests; run python -m unittest discover -s scripts/tests -v. The release workflow chooses versions from tags and passes -PshaderxVersion=... to Gradle. Ordinary builds use the version declared in shaderx/build.gradle.kts, and library tests do not need publishing secrets.
Project structure¶
| Path | Responsibility |
|---|---|
shaderx/src/commonMain |
Public effects, parameters, uniforms, results, and Compose modifiers |
shaderx/src/androidMain |
Android AGSL factory and image-processing implementation |
shaderx/src/skiaMain |
Shared Skia implementation for iOS, JVM, and Wasm |
shaderx/src/iosMain, jvmMain, wasmJsMain |
Platform factory creation |
shaderx/src/commonTest |
Effect, parameter, uniform, and result contracts |
shaderx/src/jvmTest |
Skia factory, texture, and session integration tests |
samples/ShaderLab |
Gallery plus sample-only image/audio modules |
samples/ASCIICamera, samples/VerticalCarousel |
Focused Android examples |
docs |
Handwritten guides and documentation site assets |
Add an effect¶
- Add the implementation in
shaderx/src/commonMain/kotlin/com/debanshu/shaderlab/shaderx/effect/implwith a unique ID and KDoc. - Inside the library, use
AbstractRuntimeShaderEffectorAbstractAnimatedShaderEffectand the internal typed parameter handlers. External app effects use the public interface instead. - Keep effect instances immutable. For animated effects, exclude time and live textures from structural equality; include configuration parameters.
- Declare
uniform shader content;and ahalf4 main(float2 fragCoord)entry point. Match all Kotlin uniforms to the shader declarations. - Register built-in effects in
ShaderX.builtInEffects(). For a native effect, implement it in both Android and Skia factory branches. - Add appropriate parameter/contract tests and a shader-compilation check. Validate the appearance on relevant sample targets.
- Update the README effect list, effect catalog, and changelog. Mark unreleased changes separately and state availability accurately.
The library enables Kotlin explicit API mode. New public declarations need explicit visibility and types; keep implementation details internal. Put dependencies in gradle/libs.versions.toml.
Try the library in another project¶
On macOS with the required target toolchains:
./gradlew :shaderx:publishToMavenLocal
In the consuming project's dependency repositories, temporarily add mavenLocal() before mavenCentral(), then use the local version declared in shaderx/build.gradle.kts. Do not add mavenLocal() to production setup instructions. For work in this repository, ShaderLab and ASCIICamera already depend on the local project.
API compatibility checks¶
Before changing an existing public API, compare it with the published artifact as well as the current source. Preserve positional and default arguments, generated data-class methods, and concrete return types that consumer code can use.
WaveCompatibilityTest covers the published WaveEffect copy and destructuring API, concrete return types, configuration-only equality, and shared immutable metadata. ComposeShaderLifecycleTest invokes both modifier callbacks through positional, named, trailing-lambda, and explicit frameDataKey calls. It also uses a custom factory implementing only the 0.1.3 methods and verifies that the default animated-session result reports UnsupportedEffect.
./gradlew :shaderx:jvmTest --tests '*WaveCompatibilityTest' --tests '*ComposeShaderLifecycleTest'
These are focused source and behavior regression checks. They do not establish binary compatibility for every Kotlin target or consumer toolchain; verify affected published signatures and consumer builds before a release.
Performance validation¶
Measure a specific workload before changing shader arithmetic or caching policy. Record the platform, device/GPU, build mode, viewport size, effect parameters, and whether the shader cache is cold or warm. Compare frame times and allocations at the same settings; a JVM allocation count is not a GPU frame-rate measurement.
The built-in animated effects share immutable shader sources and parameter handlers across frame copies. Run the optional JDK 21 allocation diagnostic to measure the remaining withTime() object allocations:
SHADERX_ALLOCATION_BENCHMARK=1 ./gradlew :shaderx:jvmTest --tests '*AnimatedEffectAllocationBenchmark' --rerun-tasks
The report in shaderx/build/test-results/jvmTest/TEST-com.debanshu.shaderlab.shaderx.AnimatedEffectAllocationBenchmark.xml contains bytes per copy after warmup. The diagnostic keeps results alive through a volatile sink and has no timing threshold; values depend on the JVM and do not include uniform binding, native allocations, or GPU execution. It is skipped unless the environment variable is set. Existing effect contract tests cover parameter updates, time, and equality.
Cache and native-lifetime tests cover access-order eviction, failed compilation preserving cached entries, session cleanup, active sessions surviving cache eviction, and actual pixel output after temporary Skia wrappers are released. Compose lifecycle tests cover live-data replacement, colliding hashes, factory replacement, and manual animation. The sample audio cache tests verify that repeated snapshots reuse their texture while changed data or bin counts produce an update:
./gradlew :shaderx:jvmTest :samples:ShaderLab:audiolib:jvmTest :samples:ShaderLab:imagelib:jvmTest
./gradlew :shaderx:testAndroidHostTest
Android host tests verify that unsupported offline export fails before decoding, compiling, or allocating graphics resources; they deliberately call no Android APIs and are isolated from common bitmap tests that need a device. JVM image tests check bounded dimensions, pixels, transparency, invalid input, and preservation of original bytes.
The optional FFT diagnostic compares one-shot processing and a reused workspace in the same JVM with alternating rounds; it reports allocations and median CPU time, with no timing threshold:
SHADERX_AUDIO_BENCHMARK=1 ./gradlew :samples:ShaderLab:audiolib:jvmTest --tests '*SpectrumAllocationBenchmark' --rerun-tasks
Use the sample applications to inspect animated output and transitions. Profile camera tab disposal, large-photo selection, microphone start/stop, and long-running animation on actual target devices. Sample image decoding bounds retained display bitmaps while keeping original bytes for export. The sample audio pipeline reuses FFT working buffers; Android/desktop device work runs on an IO worker with tests for cancellation and restart ownership. Device-specific decode peaks, audio drivers, and GPU frame times still require profiling on actual hardware. Keep Skia export factories separate from UI factories, as explained in image processing.
Preview and check documentation¶
Guide previews use Python 3.11+. The complete checked site also includes the production Wasm playground, so install JDK 21 and the Android SDK from local setup; Apple tooling is not required for that bundle:
python3 -m venv .venv-docs
.venv-docs/bin/python -m pip install -r requirements-docs.txt
.venv-docs/bin/python scripts/check_docs.py
.venv-docs/bin/python -m unittest discover -s scripts/tests -v
./gradlew :samples:ShaderLab:composeApp:wasmJsBrowserDistribution --no-daemon
.venv-docs/bin/zensical build --strict
.venv-docs/bin/python scripts/package_playground.py
.venv-docs/bin/python scripts/check_site.py
.venv-docs/bin/zensical serve
Build the Wasm distribution again after sample or library changes. Re-run package_playground.py after each site build because the site generator clears its output. zensical serve previews the guides; to inspect the combined artifact, serve site/ with python -m http.server --directory site 8000.
On Windows, use .venv-docs\Scripts\python and .venv-docs\Scripts\zensical. Open the URL printed by Zensical. Guides should also remain readable as ordinary Markdown on GitHub. Use descriptive page titles, accurate examples, relative links between guides, and a unique description in each page's front matter.
The maintainer guide covers documentation publishing, repository discovery settings, and release updates.
Read AGENTS.md for the change-to-guide map used by coding agents. Every codebase change requires updates to both README.md and the affected Markdown guides in docs/ in the same PR, including tests, refactors, configuration, scripts, and workflows. The Documentation PR check fails if either update is missing; there is no PR-body opt-out. Prose-only and media-only changes do not require companion edits. See the maintainer guide for the policy and examples of useful internal-change documentation.
The site publishes automatically only after a PR merges into main and its documentation build passes. Direct pushes, preview builds, and unmerged PR closures do not deploy. The documentation URL is debanshu777.github.io/ShaderX.