registerTexture — accepts an image source, assigns an opaque id, stores in
a module-level registry. Actual GL upload happens lazily at draw time in
drawShader() via GLTextureCache.upload (which is idempotent).
Lifecycle: textures live for the renderer's lifetime. No unregister in v1.
Convention §9: this registry stores image data only — no per-renderer state.
Each WeaselRenderer's GLTextureCache does its own dedup via has(id).
registerTexture — accepts an image source, assigns an opaque id, stores in a module-level registry. Actual GL upload happens lazily at draw time in drawShader() via GLTextureCache.upload (which is idempotent).
Lifecycle: textures live for the renderer's lifetime. No unregister in v1.
Convention §9: this registry stores image data only — no per-renderer state. Each WeaselRenderer's GLTextureCache does its own dedup via has(id).