Async Embedded Texture Decode Plan
Goal: reduce the remaining main-thread spike during glTF load by moving embedded image decoding off-thread, leaving only GL upload on the main thread.
Current behavior
gltf-meshcallstextures.load-texture-from-bytesfor embedded image buffer views.load-texture-from-bytesusesstbi_load_from_memoryon the main thread.- Texture generation and GL upload run on the main thread as part of that call.
Proposed change
Add a new jobs handler that decodes image bytes to raw pixels.
- Location:
src/resource_manager.cpporsrc/cgltf_jobs.cpp(preferred:resource_manager.cppalongsideload_texture/load_cubemapjobs). - Job kind:
decode_image_bytes(ordecode_texture_bytes). - Input payload: raw bytes of the image (already extracted from the glTF buffer view).
- Output:
JobResult::payloadwith the pixel bytes andaux_a/aux_b/aux_cset to width/height/channels. - Use
stbi_load_from_memoryinside the job handler; free pixels viastbi_image_freeusingNativePayload::from_owned.
- Location:
Add a Lua binding to request decode and finish on the main thread.
- Option A: expose a new
textures.load-texture-from-bytes-async(name, bytes, callback).- Submit decode job, then in callback: allocate
Texture2D, callload_from_pixels,generate(), and return the texture.
- Submit decode job, then in callback: allocate
- Option B: add a
jobscallback path used bygltf-meshdirectly to convert the decoded pixels into a texture. - Keep GPU upload on main thread; only decoding should happen in the job.
- Option A: expose a new
Update
gltf-meshto use the async decode path for embedded images.- For
image-bytes, submit the decode job and register a callback that replaces the texture in the batch when ready. - Until ready, keep the batch hidden or render with a placeholder texture.
- Make sure cancellation on
dropdoes not leak decoded buffers.
- For
Add tests (not in the main test suite).
- A dedicated Fennel test module that:
- Submits a decode job with a known embedded image (e.g. from
BoxTextured.glb). - Asserts
width/height/channelsare returned and byte count matches. - Creates a texture on the main thread using returned pixels.
- Submits a decode job with a known embedded image (e.g. from
- Keep this test opt-in, similar to other slow cgltf tests.
- A dedicated Fennel test module that:
Notes
- The job result payload already supports binary data via
NativePayload, so no extra copy into Lua tables is needed. - If you reuse
ResourceManagerto finish textures, follow the same pattern asload_texturejob handling to avoid duplicating GL upload logic.
