Skip to content

Commit 1860062

Browse files
gajopclaude
andcommitted
Fold blocking texture-export note into todo.md #10
The editor-freeze-on-export is the existing TODO #10 (background texture IO). Sharpen #10 with the concrete site and the finding that the readback binding (`gfx().read_pixels`) already exists — so it needs no engine work, just an off-thread encode, tiled to avoid OOM on large maps. Trim the code comment to a pointer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c381851 commit 1860062

2 files changed

Lines changed: 20 additions & 13 deletions

File tree

‎docs/porting/todo.md‎

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,23 @@ in particular can stall the editor for a noticeable time.
343343
**How (sketch).** Split the GPU part from the CPU part: do the minimal on-thread
344344
GL work to get the pixels off the GPU (readback into a CPU buffer), then hand the
345345
CPU-side encode + file write to a background worker (the same off-thread IO path
346-
grass/metal/heightmap use). Needs a way to read a texture's pixels into a plain
347-
buffer on the engine thread, after which the ops become "readback (on-thread) →
348-
encode+write (off-thread)". Investigate whether a shared background
349-
rendering/IO context is feasible for the readback itself.
346+
grass/metal/heightmap use). The ops become "readback (on-thread) →
347+
encode+write (off-thread)".
348+
349+
**Concrete site + finding (2026-07-21).** The blocking path is
350+
[textures/project.rs](../../native/src/sbc/textures/project.rs) `export_map_textures`
351+
→ `export_diffuse`/`export_shading_textures` → `save_texture_png`
352+
([graphics.rs](../../native/src/sbc/textures/model/texture_model/graphics.rs)),
353+
which calls the engine's `gfx().save_image` — one blocking call that reads the GL
354+
texture *and* encodes+writes the PNG on the draw thread, freezing the editor for
355+
the whole export (user-observed several-second stall). The readback binding
356+
already exists: `gfx().read_pixels(x, y, w, h, format) -> (Vec<f32>, format)`, and
357+
`io::write::save_png` already encodes off-thread (metal/grass/heightmap use it).
358+
So no engine work is needed — just: read pixels on the draw thread, encode in an
359+
`IoJob`. Two cares: (1) convert the `f32` channels back to `u8`; (2) **tile the
360+
readback** — a full-map diffuse as `read_pixels`' f32 RGBA is ~1 GB at 8K, so a
361+
whole-texture buffer would OOM on large maps. That tiling is why this is its own
362+
careful effort, not a quick swap.
350363

351364
## 11. Texture model uses `RefCell` — remove the runtime-panic surface
352365

‎native/src/sbc/textures/project.rs‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,9 @@ fn load_textures(ctx: &mut Context, paths: &ProjectPaths) {
4444
log_result("load textures", &dir, result);
4545
}
4646

47-
// TODO(perf): these run on the draw thread and freeze the editor during export.
48-
// `save_texture_png` calls the engine's `gfx().save_image`, which reads the GL
49-
// texture AND encodes+writes the PNG in one blocking call. The heightmap/metal/
50-
// grass exports already encode off-thread (`*::jobs::export`); the diffuse and
51-
// shading textures should too: read the pixels on the draw thread via
52-
// `gfx().read_pixels` (only the GL read must be here), then hand the buffer to an
53-
// IoJob that encodes with `io::write::save_png`. Do it in tiles — a full-map
54-
// diffuse read back as `read_pixels`' f32 RGBA is ~1 GB at 8K, so a naive
55-
// whole-texture buffer would OOM on large maps.
47+
// These `save_texture_png` calls block the draw thread (GL read + PNG encode in
48+
// the engine's `save_image`), freezing the editor during export. Moving the
49+
// encode off-thread is docs/porting/todo.md #10.
5650
fn export_map_textures(ctx: &mut Context, output_dir: &Path, _options: &MapExportOptions) {
5751
let interface = *ctx.interface;
5852
let diffuse = output_dir.join("diffuse.png");

0 commit comments

Comments
 (0)