Project

glslkit

0.0
The project is in a healthy, maintained state
Flattens #include directives in GLSL shaders and extracts uniform/attribute/output reflection data as JSON, with zero runtime dependencies so it can run under ruby.wasm.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

glslkit

GLSLの #include 解決とリフレクション抽出を行うRuby gem。シェーダのコンパイル エラーが、#include先の元ファイル名と行番号を持ったRubyの例外として上がる。

CompileErrorがcommon/sdf.glsl:5を指しているneon-error.htmlの画面

なぜこれが要るのか

GLSLには #include が無く、シェーダをファイル単位でモジュール分割できない。 コピー&ペーストで共通コードを重複させるか、1ファイルに書き殴るかの二択になりやすい。 また、コンパイルエラーはドライバから「平坦化後の行番号」でしか返らないため、 #includeを手動展開している場合、エラー行から元のファイルを探し当てるのは それ自体が調査作業になる。glslkitは#include解決を肩代わりし、平坦化前の ファイル名・行番号をエラーに復元して返す。

構成

1リポジトリ・3gem構成。core/が将来 ruby.wasm にバンドルされる際、Rails側のコードと依存を持ち込まないための分離。

gem ディレクトリ 実行時依存 内容
glslkit core/ なし #include解決、リフレクション抽出、マニフェスト生成
glslkit-rails rails/ glslkit, railties >= 7.1, propshaft >= 1.3.2 Propshaft統合、ビューヘルパー、ライブリロード
glslkit-webgl webgl/ glslkit ruby.wasm上で動くWebGL2ランタイム(ブラウザ向け)
glslkit/
├── core/                             # gem: glslkit
├── rails/                            # gem: glslkit-rails
├── webgl/                            # gem: glslkit-webgl
├── examples/rails-demo/              # ライブリロードの実演用Railsアプリ
└── spec/schema/reflection-v1.json    # JSON Schema (Draft 2020-12)

インストールと最小の使用例

gem "glslkit"
require "glslkit"

resolver = Glslkit::Resolvers::FileSystem.new(load_paths: ["app/shaders"])
source   = Glslkit::Preprocessor.new(resolver: resolver).process("pbr.frag")

source.code        # => 平坦化済みGLSL (String)。#include解決済み、#lineディレクティブ付き
source.reflection  # => Glslkit::Reflection (attributes / uniforms / uniform_blocks / outputs)
source.digest       # => SHA256 hex (String)

Railsで使う場合、wasm上でWebGLを扱う場合はそれぞれのREADMEを参照:

  • core/README.md — #include解決、リフレクション抽出、マニフェスト生成の詳細
  • rails/README.md — Propshaft統合、ビューヘルパー、rakeタスク
  • webgl/README.md — ruby.wasm上でのWebGL2ランタイム、rbwasmでのパッケージング

glslkit-webglのページはfile://ではなくHTTP経由で開くこと。 ruby.wasmが エントリの.rbをfetchで読み込むため、file://ではSafariが確実に失敗する (Fetch API cannot load file:///... due to access control checks.)。Chromeは 現状緩めに動くが将来同じ制限が入る可能性がある。python3 -m http.server 8000 等でローカルサーバを立ててhttp://localhost:8000/...として開くこと(詳細は webgl/README.md参照)。

ライブリロード

.glsl/.vert/.fragを保存すると、ページをリロードせずにシェーダだけ 差し替わる。壊すと直前の描画を保ったまま元ファイル名・行番号でエラーが出て、 直すとリロード無しで復帰する。実際に動かして試せる最小Railsアプリを examples/rails-demo/に用意している (development専用)。

実測値(Chrome)

  • 描画: 60fps / 30秒1801フレームでフレーム落ちゼロ / 最悪フレーム17.7ms
  • GC: 計測中に12回発生したが、発生フレームも通常フレームと区別がつかなかった

起動コスト

ruby.wasmのバイナリ(ruby+stdlib.wasm)は約9MBある。

  • 初回訪問(キャッシュ無し): 約9MBの転送 + 起動まで中央値約1,130ms
  • 2回目以降(HTTPキャッシュ有効): 中央値117〜178ms(fetch+compile / VM初期化 / 最初の行の実行、の合計。計測環境により倍近い差が出る)

いずれもChrome実測。ネットワーク環境により初回は大きく変動する。

ruby.wasmにはstdlibを絞ったビルドもあり、glslkit-webglが実際に使うstdlibは 限られるため、カスタムビルドで転送量を減らせる可能性がある(未検証)。

できないこと

以下は実測に基づく制約であり、設計上の欠陥ではない(詳細は webgl/README.md参照):

  • 毎フレームの頂点データ更新
  • CPUスキニング
  • モーフターゲットのCPU補間
  • パーティクル(CPU側で毎フレーム更新するもの)

100,000要素の転送だけで18.8msかかり、60fpsのフレーム予算を超える。 ジオメトリは静的に保ち、変形はシェーダ側(GPU)で行い、行列やウェイトだけを uniformとして送ること。

検証環境

  • Chrome: 描画・フレーム安定性・起動コスト・ライブリロードを実測
  • Safari: 描画のみ確認(neonサンプルが正しく動作)。フレーム性能とライブリロードは未計測

いずれもmacOS。他のブラウザ・OSは未確認。

ライセンス

MIT