← Back

ofxMetalGLStorageBridge

github.com/pit11511/ofxMetalGLStorageBridge · @pit11511
OF 0.12 Graphics Bridges macOS

macOS addon sharing GPU storage between Metal compute and OpenGL through IOSurface textures, avoiding per-frame CPU copies.

1 0 Last commit 5mo ago Created 6mo ago

README

ofxMetalGLStorageBridge



ofxMetalGLStorageBridge は、macOS 上で

Metal compute -> IOSurface ベースの共有ストレージテクスチャ -> OpenGL 読み出し

を行うための openFrameworks addon です。

通常経路では CPU copy を前提にせず、IOSurface を使って Metal と OpenGL が同じ underlying storage を共有します。

想定プラットフォーム



なぜ IOSurface を使うのか



Metal texture と OpenGL texture を別々に持つと、毎フレーム CPU 経由の upload / download や GPU 間コピー管理が必要になります。IOSurface を使うと、同じ共有メモリを

で参照できます。

この addon では、この共有面を「描画用画像」ではなく「GPU データコンテナ」として扱います。

アーキテクチャ



ストレージ指向であり draw 指向ではない



この addon の API は draw() を中心にしません。

中心になる操作は以下です。

OpenGL 側へ渡すのは「可視化用テクスチャ」ではなく「GPU データリソースの texture handle」です。

重要な OpenGL 制約



Apple の CGLTexImageIOSurface2D は、公式ヘッダ上は GL_TEXTURE_RECTANGLE を前提にした API です。 そのため本 addon では OpenGL 側ターゲットを GL_TEXTURE_RECTANGLE に固定しています。

つまり OpenGL 側の読み出しは、通常の sampler2D ではなく次を推奨します。

この方針は draw の見た目より、整数 texel 座標での厳密なデータ読み出しを優先しています。

座標系 / Y flip について



この addon ではストレージ座標を明示的に次で定義します。

注意点:

example-basic では RGBA32Float を主例として、追加で RG32Float と RGBA16Float に対しても Metal 側から

を書き込み、CPU readback と GLSL の両方で layout を確認できます。

チャンネル順序について



この addon は BGRA のような表示寄り format を既定にしません。 数値ストレージ用途を優先し、基本は以下を使います。

  • R32Float
  • RG32Float
  • RGBA16Float
  • RGBA32Float
  • RGBA8Uint
既定サンプルは RGBA32Float です。

現状の format 対応



StorageFormat状態備考
R32Float対応1ch float
RG32Float対応2ch float
RGBA16Float対応4ch half float
RGBA32Float対応4ch float
RGBA8Uint対応4ch uint8
RGBA32Uint未対応IOSurface 側 pixel format 対応を要確認
RGBA32Uint は API enum には入っていますが、現時点では allocate を失敗させます。 理由は「Metal / OpenGL / IOSurface で 128-bit RGBA uint storage を明確に対応付ける pixel format」を安全に固定できていないためです。

OpenGL 側は texelFetch 推奨



シミュレーションデータでは補間を避けるべきです。

推奨:

#version 150
uniform sampler2DRect uStorageTex;
ivec2 coord = ivec2(12, 7);
vec4 raw = texelFetch(uStorageTex, coord);


非推奨:

vec4 raw = texture(uStorageTex, uv);


texture() は補間や座標正規化の文脈と混ざりやすく、データストレージ用途では誤読の原因になります。

CPU upload / download について



upload() と download() は packed row data を受け取り、内部では IOSurface の bytesPerRow alignment を吸収します。

そのため debug / 初期化用途では扱いやすいですが、通常フレーム更新は GPU だけで完結させる想定です。

デバッグ / validation



以下を実装しています。

これにより次を確認しやすくしています。

さらに OpenGL 側の end-to-end 検証用として

を用意しています。これは一時 FBO に対して

で shared texture を 1:1 読み出し

で float readback を行います。

使い方の最小例



ofxSharedStorageTexture storage;
storage.allocate(256, 256, StorageFormat::RGBA32Float);
storage.setSemantic("pos.x", "pos.y", "pos.z", "life");

ofxMetalStorageCompute compute;
compute.setup();
compute.loadLibrary(ofToDataPath("metal/ofxMetalGLStorageKernels.metallib", true));
compute.loadKernel("writeDebugPattern");
compute.bindOutputStorage(storage, 0);
compute.dispatchForTextureSize();
compute.waitUntilCompleted();

GLuint glTex = storage.getGLTextureID();
GLenum glTarget = storage.getGLTextureTarget(); // GL_TEXTURE_RECTANGLE


ping-pong



反復シミュレーション用に ofxSharedStoragePingPong を入れています。

ofxSharedStoragePingPong pingPong;
pingPong.allocate(1024, 1024, StorageFormat::RGBA32Float);

compute.bindInputStorage(pingPong.front(), 0);
compute.bindOutputStorage(pingPong.back(), 1);
compute.dispatchForTextureSize();
compute.waitUntilCompleted();
pingPong.swap();


example-basic



example-basic は次を行います。

1. RGBA32Float の shared storage を確保 2. Metal の writeDebugPattern kernel を dispatch 3. waitUntilCompleted() 4. CPU download() で複数 texel をログ出力 5. 同じ texture を OpenGL の sampler2DRect + texelFetch でプレビュー 6. OpenGL 側でも texelFetch -> FBO -> glReadPixels の数値検証 7. RG32Float と RGBA16Float でも同じ CPU/GL validation

実行前に metallib を作る



production path としては compiled .metallib を使うのが安全です。

cd addons/ofxMetalGLStorageBridge
./scripts/build_metallib.sh


example-pingpong 用の particle simulation kernel を build する場合:

cd addons/ofxMetalGLStorageBridge
./scripts/build_metallib.sh \
  example-pingpong/bin/data/metal/ofxMetalGLStorageParticles.metal \
  example-pingpong/bin/data/metal/ofxMetalGLStorageParticles.metallib


xcrun metal が

cannot execute tool 'metal' due to missing Metal Toolchain

で失敗する場合は、先に以下を実行してください。

xcodebuild -downloadComponent MetalToolchain


生成先:

source fallback



example と ofxMetalStorageCompute::loadLibrary() は、.metallib が見つからない場合に .metal source の runtime compile も試せます。 ただしこれは debug / convenience 用です。production では .metallib を推奨します。

project generator



addons/ofxMetalGLStorageBridge/example-basic を project generator で開き、addon として ofxMetalGLStorageBridge を含めてください。

example-pingpong



example-pingpong は実更新ロジック付きの ping-pong simulation 例です。

Metal 側では

の 2 kernel を使います。

OpenGL 側では vertex shader が

から texelFetch し、gl_VertexID を particle index として point rendering します。

これは

という intended path の実例です。

project generator



addons/ofxMetalGLStorageBridge/example-pingpong を project generator で開き、addon として ofxMetalGLStorageBridge を含めてください。

setParams() の例



kernel に parameter struct を渡す場合は setParams() を使います。

struct ParticleSimParams {
    float deltaTime;
    float time;
    float bounds;
    float damping;
};

ParticleSimParams params;
params.deltaTime = ofGetLastFrameTime();
params.time = ofGetElapsedTimef();
params.bounds = 0.92f;
params.damping = 0.995f;

compute.setParams(params);
compute.dispatchForTextureSize();
compute.waitUntilCompleted();


注意:

制限事項



今後の候補



TODO と明示している不確定点



1. RGBA32Uint 明確な IOSurface pixel format 対応が未確定なので、現実装では unsupported にしています。 2. GL_TEXTURE_2D 対応 一部環境では動く例がありますが、Apple の CGLTexImageIOSurface2D ヘッダ記述に合わせて本 addon は GL_TEXTURE_RECTANGLE を既定にしています。 3. OpenGL imageLoad macOS driver 差異を踏まえ、README と example では texelFetch を優先しています。

Get this addon

HTTPS
SSH