Skip to content

feature: use KTX2 texture format to significantly improve load times#30

Merged
dastarruer merged 8 commits intomasterfrom
feature/fix-texture-load-performance
Mar 31, 2026
Merged

feature: use KTX2 texture format to significantly improve load times#30
dastarruer merged 8 commits intomasterfrom
feature/fix-texture-load-performance

Conversation

@dastarruer
Copy link
Copy Markdown
Owner

@dastarruer dastarruer commented Mar 29, 2026

Closes #28.

Screenshot

image

Benchmarks

Before

image
2026-03-31.16-23-35.mp4

After

image
2026-03-31.16-24-15.mp4

Summary

  • Redo normal map using Krita for my sanity
  • Reduce scale of normal map to get a less exaggerated and more realistic look (might change later)
  • Add vite-plugin-static-copy vite plugin to copy necessary transcoder files at buildtime
  • Create a custom vite plugin to convert .png textures to .ktx2 textures at build time.
    • While converting manually is an option, this gets annoying for two reasons: first, should the original texture be modified in some way, I would need to manually convert the file to .ktx2 every single time. Secondly, it would unnecessarily bloat the size of the git tree with two versions of the same data that already exist.

Summary by CodeRabbit

  • New Features

    • Globe now uses KTX2 textures for normal maps, improving load/compatibility.
    • Visuals refined: stronger normal-map intensity and corrected sphere orientation.
  • Chores

    • Build tooling updated to generate and include KTX2 assets; added image/asset utilities to dev tooling.
    • Added .ktx2 to ignore rules so generated KTX2 files aren’t committed.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

This PR converts the globe's normal map workflow from PNG to KTX2 by adding build-time PNG→KTX2 encoding, bundling the Three.js basis transcoder, switching the component to load KTX2, and adjusting related material/mesh properties.

Changes

Cohort / File(s) Summary
Ignore rule
app/.gitignore
Added *.ktx2 pattern to ignore generated KTX2 files.
Dev dependencies
app/package.json
Added @types/three, ktx2-encoder, sharp, and vite-plugin-static-copy to devDependencies.
Vite build & plugin
app/vite.config.ts
Added viteStaticCopy to bundle Three.js basis libs and a local Vite plugin that encodes static/normal-map.pngstatic/normal-map.ktx2 at buildStart with timestamp-based skip, synchronous file I/O, sharp-based image decoding, and error handling adjustments.
Globe UI
app/src/lib/components/globe/Globe.svelte
Switched texture loading to KTX2 via useKtx2(...).loadAsync(), removed post-load setTimeout segment-increase, changed normalScale to [1.5,1.5], and flipped mesh scale to [1,-1,1] to correct normal orientation.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Dev as Developer / Build
participant Vite as Vite (build)
participant Plugin as KTX2 Plugin (Node)
participant FS as File System
participant Basis as Three.js Basis libs (static)
participant Client as Browser / Globe Component

Dev->>Vite: start build
Vite->>Plugin: run buildStart hook
Plugin->>FS: stat png & ktx2 files
alt ktx2 up-to-date
    Plugin->>Vite: skip conversion (log)
else convert needed
    Plugin->>FS: read PNG
    Plugin->>Plugin: decode via sharp -> raw RGBA
    Plugin->>Plugin: encodeToKTX2(...)
    Plugin->>FS: write KTX2
end
Vite->>Basis: viteStaticCopy copies basis libs -> dist root
Client->>Basis: fetch basis transcoder
Client->>Client: useKtx2().loadAsync('/normal-map.ktx2')
Client->>Client: apply normalMap, set normalScale, render flipped mesh

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibble pixels, crisp and spry,

PNG to KTX, a blip goodbye.
Basis bundled, build hops light,
Globe normals gleam through speed and night. 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: switching to KTX2 texture format for improved performance, which directly addresses the core objective.
Linked Issues check ✅ Passed The PR successfully implements all requirements from issue #28: converts normal map from PNG to KTX2 format, implements build-time conversion logic, adds necessary transcoder dependencies, and updates the Globe component to load KTX2 textures.
Out of Scope Changes check ✅ Passed All changes are directly related to the KTX2 implementation objective. The .gitignore, dependencies, Vite configuration, and Globe component modifications are all necessary for the feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/fix-texture-load-performance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dastarruer dastarruer force-pushed the feature/fix-texture-load-performance branch 2 times, most recently from e47a1ec to 16b8a0e Compare March 31, 2026 16:28
@dastarruer dastarruer force-pushed the feature/fix-texture-load-performance branch from 16b8a0e to ee4665b Compare March 31, 2026 17:03
@dastarruer dastarruer changed the title feature: use KTX2 format to significantly improve performance feature: use KTX2 texture format to significantly improve load times Mar 31, 2026
@dastarruer dastarruer marked this pull request as ready for review March 31, 2026 20:27
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/.gitignore`:
- Around line 25-26: The current .gitignore rule '*.ktx2' is too broad; restrict
it to only generated build outputs by replacing that line with a scoped pattern
that targets generated artifacts (e.g., change the '*.ktx2' entry to a
build-only pattern such as '**/build/**/*.ktx2' or 'app/**/build/**/*.ktx2') so
checked-in KTX2 assets aren't accidentally ignored; update the '*.ktx2' entry
accordingly in the .gitignore.

In `@app/src/lib/components/globe/Globe.svelte`:
- Around line 17-25: The load chain using
useKtx2(...).loadAsync('/normal-map.ktx2') currently has no rejection handling
so failures leave isLoaded false and the globe stuck in wireframe; add a .catch
handler (or convert to async/await with try/catch) on the promise returned by
loadAsync in Globe.svelte to handle errors from useKtx2/loadAsync, set isLoaded
= true or to a safe fallback state, optionally clear or set normalMap to null,
set numSegments to a reasonable default, and log the error so failures don’t
leave the component indefinitely in the loading/wireframe state; reference the
useKtx2 call, the loadAsync promise, and the isLoaded/normalMap/numSegments
variables when implementing this change.

In `@app/vite.config.ts`:
- Around line 27-40: The build currently invokes execFileSync('toktx', ...) (the
toktx call that produces normal-map.ktx2) which makes vite build fail when the
external toktx binary isn't present; instead, change the build step around that
execFileSync call to either (A) guard it: detect availability of toktx (e.g.,
try spawnSync/which or wrap execFileSync in try/catch) and fall back to a
checked-in normal-map.ktx2 or a JS-based encoder, or (B) replace the external
dependency by adding a reproducible/npm-provided encoder and invoke its binary
from node_modules/.bin (so npm install provisions it) or commit normal-map.ktx2;
ensure the code references the same artifact name (normal-map.ktx2) and that
vite build no longer assumes an unmanaged toktx binary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5f4d1d1c-bb8b-42b4-a667-cec77c6fd449

📥 Commits

Reviewing files that changed from the base of the PR and between 9faa30e and 5f75a07.

⛔ Files ignored due to path filters (2)
  • app/package-lock.json is excluded by !**/package-lock.json
  • app/static/normal-map.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • app/.gitignore
  • app/package.json
  • app/src/lib/components/globe/Globe.svelte
  • app/vite.config.ts
  • devenv.nix

Comment thread app/.gitignore
Comment thread app/src/lib/components/globe/Globe.svelte
Comment thread app/vite.config.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/vite.config.ts`:
- Line 55: The success log currently prints the literal "${output}" because it's
using single quotes; update the console.log call (console.log('ktx2 successfully
created ${output}')) to actually interpolate the output variable by using a
template string or concatenation, e.g. change to console.log(`ktx2 successfully
created ${output}`) or console.log('ktx2 successfully created ' + output) so the
generated file path is shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8932c627-e9de-4dda-8ba6-491822c38305

📥 Commits

Reviewing files that changed from the base of the PR and between 5f75a07 and af7668d.

⛔ Files ignored due to path filters (1)
  • app/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • app/package.json
  • app/vite.config.ts

Comment thread app/vite.config.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/vite.config.ts`:
- Around line 27-59: The code is using an undocumented imageDecoder option with
encodeToKTX2; remove the entire imageDecoder async callback (and its sharp-based
pixel decoding) and instead pass the raw pngBuffer directly to
encodeToKTX2(pngBuffer, options). Keep the existing encoder option keys
(isUASTC, qualityLevel, isNormalMap, generateMipmap, isKTX2File,
needSupercompression) but ensure they match the ktx2-encoder v0.5.1 API; update
or remove unused sharp-related code/imports and only handle the resulting
ktx2Buffer/writeFileSync logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d72ca498-3b57-4a84-8616-3c8fffdc9eb7

📥 Commits

Reviewing files that changed from the base of the PR and between af7668d and 0dd323b.

📒 Files selected for processing (1)
  • app/vite.config.ts

Comment thread app/vite.config.ts
@dastarruer dastarruer merged commit 2760b3c into master Mar 31, 2026
5 checks passed
@dastarruer dastarruer deleted the feature/fix-texture-load-performance branch March 31, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix poor performance when loading normal map for globe

1 participant