Skip to content

viewer: survive block-misaligned KTX2 textures on WebGPU#467

Merged
wass08 merged 1 commit into
mainfrom
fix/viewer-ktx2-alignment
Jul 6, 2026
Merged

viewer: survive block-misaligned KTX2 textures on WebGPU#467
wass08 merged 1 commit into
mainfrom
fix/viewer-ktx2-alignment

Conversation

@wass08

@wass08 wass08 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Fixes the community /viewer black-screen + endless Invalid BindGroup "bindGroup_object" / Invalid CommandBuffer validation spam on baked GLBs that carry a KTX2 texture with non-multiple-of-4 dimensions (e.g. a 299×399 user-item JPEG that the bake pipeline encoded unresized).

WebGPU rejects block-compressed textures whose base dimensions aren't multiples of 4. three's KTX2Loader only warns about such payloads and transcodes them to a block format anyway; the resulting invalid texture then poisons every render pass that binds it, once per frame.

ktx2-loader.ts now exports an AlignmentSafeKTX2Loader: it reads the width/height straight from the KTX2 header for Basis payloads (vkFormat 0), and routes misaligned ones through a fallback loader whose support flags report no compressed formats — the transcoder then emits plain RGBA32, which has no alignment requirement. The decoded pixels are repackaged as a regular DataTexture because the loader's RGBA output still arrives wrapped in a CompressedTexture, and three's WebGPU upload path (WebGPUTextureUtils._copyCompressedBufferToTexture) crashes looking up a block descriptor for rgba8. Aligned textures take the unchanged fast path.

Companion private-editor PR fixes the bake LOD encoder to power-of-two-resize JPEG sources too, so new artifacts won't contain misaligned KTX2 in the first place; this change is what makes the already-baked prod artifacts render.

How to test

  1. Reproduced with prod artifact project_dAYO2MJVTHExwsQY (lod0.glb contains one 299×399 ETC1S KTX2 texture): served the GLB locally, pointed a local projects_bakes row at it, and drove /viewer/<id> with WebGPU-enabled headless Chromium (--enable-unsafe-webgpu --use-angle=metal). Before: 4,025 GPU validation errors in 60s, black scene. After: 0 errors, scene renders, single console warning KTX2 texture is 299x399 (not multiple-of-4); transcoding uncompressed….
  2. Regression: a healthy scene (aligned KTX2 textures, catalog finishes) renders with 0 validation errors and never hits the fallback path.
  3. bun run check-types and bunx biome check pass.

Screenshots / screen recording

N/A — non-visual change when textures are well-formed; for the affected artifacts the change is "black screen + error spam" → "renders normally".

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

🤖 Generated with Claude Code


Note

Medium Risk
Changes the central KTX2 load path for all GLB and catalog textures and relies on private three.js loader internals, though aligned textures are unchanged and the fallback is narrowly scoped to misaligned Basis payloads.

Overview
Fixes WebGPU black screens and per-frame Invalid BindGroup / Invalid CommandBuffer spam when baked GLBs include Basis KTX2 textures whose width or height are not multiples of 4 (e.g. odd-sized user-item images).

The shared ktx2Loader is now an AlignmentSafeKTX2Loader that inspects the KTX2 header and, for misaligned Basis payloads, transcodes via a fallback loader with all compressed formats disabled so output is RGBA32, then wraps the result in a DataTexture (three’s WebGPU path still treats RGBA from CompressedTexture as block-compressed). Aligned textures keep the existing fast path through stock KTX2Loader._createTexture.

Reviewed by Cursor Bugbot for commit d2448ac. Bugbot is set up for automated code reviews on this repo. Configure here.

WebGPU rejects block-compressed textures whose base dimensions aren't
multiples of 4; three's KTX2Loader only warns and transcodes anyway, so a
baked GLB carrying one odd-sized user-item texture (e.g. 299x399) poisoned
every render pass — endless 'Invalid BindGroup … Invalid CommandBuffer'
spam. Route such payloads through a fallback transcode to uncompressed
RGBA32 and repackage the pixels as a plain DataTexture (three's WebGPU
upload path crashes on a CompressedTexture holding rgba8 data).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d2448ac. Configure here.

texture.generateMipmaps = true
texture.minFilter = LinearMipmapLinearFilter
texture.magFilter = LinearFilter
texture.needsUpdate = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Repackaged texture omits flipY

Medium Severity

The misaligned path builds a new DataTexture from decoded pixels but only copies colorSpace and filter settings from the transcoded result. Loader-applied fields such as flipY stay at DataTexture defaults, so GLB and catalog textures on this path can render vertically flipped versus the aligned compressed path.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d2448ac. Configure here.

@wass08 wass08 merged commit f6a5848 into main Jul 6, 2026
3 checks passed
@mintlify

mintlify Bot commented Jul 6, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
pascal 🔴 Failed Jul 6, 2026, 11:43 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

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.

1 participant