@forgeax/engine-assets-runtime 0.1.3 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +442 -175
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/aabb.unit.test.d.ts +2 -0
- package/dist/__tests__/aabb.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-integrity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts +2 -0
- package/dist/__tests__/artifact-io.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-sdk.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-kind.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.integration.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog-reconcile.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry-catalog.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry-catalog.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.pack-cache.test.d.ts.map +1 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/asset-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/asset-runtime-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-replica.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source-v2.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-source.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription-order.unit.test.d.ts.map +1 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts +2 -0
- package/dist/__tests__/catalog-subscription.unit.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes-pass-through.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.browser.test.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test-d.d.ts.map +1 -0
- package/dist/__tests__/decode-image-bytes.test.d.ts +2 -0
- package/dist/__tests__/decode-image-bytes.test.d.ts.map +1 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts +2 -0
- package/dist/__tests__/dynamic-texture-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors-asset.unit.test.d.ts +2 -0
- package/dist/__tests__/errors-asset.unit.test.d.ts.map +1 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts +2 -0
- package/dist/__tests__/inline-pack-loaders.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-context.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate-publication-fence.unit.test.d.ts.map +1 -0
- package/dist/__tests__/instantiate.unit.test.d.ts +2 -0
- package/dist/__tests__/instantiate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-input-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts +2 -0
- package/dist/__tests__/loader-registry.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-dependency-invalidation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/material-generation-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts +2 -0
- package/dist/__tests__/material-load-consumer.test-d.d.ts.map +1 -0
- package/dist/__tests__/material-ready.integration.test.d.ts +2 -0
- package/dist/__tests__/material-ready.integration.test.d.ts.map +1 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts +2 -0
- package/dist/__tests__/material-stale-generation.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts +2 -0
- package/dist/__tests__/mipmap-generator.unit.test.d.ts.map +1 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts +2 -0
- package/dist/__tests__/pack-artifact-transcode-model-owner.test.d.ts.map +1 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts +2 -0
- package/dist/__tests__/pack-basis-load.integration.test.d.ts.map +1 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts +2 -0
- package/dist/__tests__/payload-validate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +2 -0
- package/dist/__tests__/plugin.test.d.ts.map +1 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts +2 -0
- package/dist/__tests__/registry-concurrency.bench.test.d.ts.map +1 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-failure-retry.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/registry-load-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-load-state.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-package-cache.unit.test.d.ts.map +1 -0
- package/dist/__tests__/registry-public-ready.unit.test.d.ts +2 -0
- package/dist/__tests__/registry-public-ready.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-handle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-handle-fields.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-handle-fields.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts +2 -0
- package/dist/__tests__/scene-payload.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts +2 -0
- package/dist/__tests__/scene-publication-fence.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-public-surface.test-d.d.ts.map +1 -0
- package/dist/__tests__/static-rig.integration.test.d.ts +2 -0
- package/dist/__tests__/static-rig.integration.test.d.ts.map +1 -0
- package/dist/__tests__/validate-material.unit.test.d.ts +2 -0
- package/dist/__tests__/validate-material.unit.test.d.ts.map +1 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts +2 -0
- package/dist/__tests__/video-pack-loader.integration.test.d.ts.map +1 -0
- package/dist/aabb.d.ts +3 -0
- package/dist/aabb.d.ts.map +1 -0
- package/dist/asset-registry.d.ts +552 -0
- package/dist/asset-registry.d.ts.map +1 -0
- package/dist/builtin-asset-registry.d.ts +22 -0
- package/dist/builtin-asset-registry.d.ts.map +1 -0
- package/dist/catalog-source.d.ts +2 -1
- package/dist/catalog-source.d.ts.map +1 -1
- package/dist/decode-image-bytes.d.ts +57 -0
- package/dist/decode-image-bytes.d.ts.map +1 -0
- package/dist/dynamic-texture-store.d.ts +89 -0
- package/dist/dynamic-texture-store.d.ts.map +1 -0
- package/dist/errors/asset.d.ts +201 -0
- package/dist/errors/asset.d.ts.map +1 -0
- package/dist/handles.d.ts +141 -0
- package/dist/handles.d.ts.map +1 -0
- package/dist/image-error.d.ts +6 -0
- package/dist/image-error.d.ts.map +1 -0
- package/dist/index.d.ts +32 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +7218 -114
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +317 -3
- package/dist/internal.mjs.map +1 -1
- package/dist/loader-registry.d.ts +63 -0
- package/dist/loader-registry.d.ts.map +1 -0
- package/dist/loaders/inline-pack.d.ts +72 -0
- package/dist/loaders/inline-pack.d.ts.map +1 -0
- package/dist/loaders/mesh-bin.d.ts +21 -0
- package/dist/loaders/mesh-bin.d.ts.map +1 -0
- package/dist/loaders/pack-artifact.d.ts +20 -0
- package/dist/loaders/pack-artifact.d.ts.map +1 -0
- package/dist/material/dependency-graph.d.ts +9 -0
- package/dist/material/dependency-graph.d.ts.map +1 -0
- package/dist/material/generation-cache.d.ts +24 -0
- package/dist/material/generation-cache.d.ts.map +1 -0
- package/dist/material/inspection.d.ts +24 -0
- package/dist/material/inspection.d.ts.map +1 -0
- package/dist/material/loader.d.ts +59 -0
- package/dist/material/loader.d.ts.map +1 -0
- package/dist/material/runtime-shader.d.ts +14 -0
- package/dist/material/runtime-shader.d.ts.map +1 -0
- package/dist/mesh-bin.d.ts +3 -0
- package/dist/mesh-bin.d.ts.map +1 -0
- package/dist/mipmap-generator.d.ts +132 -0
- package/dist/mipmap-generator.d.ts.map +1 -0
- package/dist/payload-validate.d.ts +60 -0
- package/dist/payload-validate.d.ts.map +1 -0
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/registry/artifact-io.d.ts +16 -0
- package/dist/registry/artifact-io.d.ts.map +1 -0
- package/dist/registry/asset-evidence.d.ts +13 -0
- package/dist/registry/asset-evidence.d.ts.map +1 -0
- package/dist/registry/catalog-state.d.ts +47 -0
- package/dist/registry/catalog-state.d.ts.map +1 -0
- package/dist/registry/catalog.d.ts +48 -0
- package/dist/registry/catalog.d.ts.map +1 -0
- package/dist/registry/instantiate.d.ts +153 -0
- package/dist/registry/instantiate.d.ts.map +1 -0
- package/dist/registry/load-by-guid.d.ts +247 -0
- package/dist/registry/load-by-guid.d.ts.map +1 -0
- package/dist/registry/load-state.d.ts +26 -0
- package/dist/registry/load-state.d.ts.map +1 -0
- package/dist/registry/runtime-projection.d.ts +16 -0
- package/dist/registry/runtime-projection.d.ts.map +1 -0
- package/dist/registry/scene-publication-fence.d.ts +38 -0
- package/dist/registry/scene-publication-fence.d.ts.map +1 -0
- package/dist/registry/validate-material.d.ts +77 -0
- package/dist/registry/validate-material.d.ts.map +1 -0
- package/dist/resolve-asset-handle.d.ts +43 -0
- package/dist/resolve-asset-handle.d.ts.map +1 -0
- package/dist/resolve-tileset-runtime.d.ts +23 -0
- package/dist/resolve-tileset-runtime.d.ts.map +1 -0
- package/dist/scene-handle-fields.d.ts +88 -0
- package/dist/scene-handle-fields.d.ts.map +1 -0
- package/dist/scene-payload.d.ts +15 -0
- package/dist/scene-payload.d.ts.map +1 -0
- package/dist/wire-default-loaders.d.ts +29 -0
- package/dist/wire-default-loaders.d.ts.map +1 -0
- package/package.json +22 -9
- package/src/__tests__/aabb.unit.test.ts +97 -0
- package/src/__tests__/artifact-integrity.unit.test.ts +99 -0
- package/src/__tests__/artifact-io.unit.test.ts +127 -0
- package/src/__tests__/asset-evidence-sdk.integration.test.ts +32 -0
- package/src/__tests__/asset-evidence.unit.test.ts +32 -0
- package/src/__tests__/{asset-kind.test.ts → asset-kind.unit.test.ts} +1 -1
- package/src/__tests__/asset-registry-catalog-reconcile.integration.test.ts +74 -0
- package/src/__tests__/asset-registry-catalog-reconcile.unit.test.ts +89 -0
- package/src/__tests__/asset-registry-catalog.unit.test.ts +170 -0
- package/src/__tests__/asset-registry.pack-cache.test.ts +96 -0
- package/src/__tests__/asset-registry.unit.test.ts +511 -0
- package/src/__tests__/asset-runtime-error-code-owner.test-d.ts +63 -0
- package/src/__tests__/catalog-replica.unit.test.ts +196 -0
- package/src/__tests__/catalog-source-v2.unit.test.ts +158 -0
- package/src/__tests__/catalog-source.unit.test.ts +126 -0
- package/src/__tests__/catalog-subscription-order.unit.test.ts +65 -0
- package/src/__tests__/catalog-subscription.unit.test.ts +28 -0
- package/src/__tests__/decode-image-bytes-pass-through.test.ts +34 -0
- package/src/__tests__/decode-image-bytes.browser.test.ts +102 -0
- package/src/__tests__/decode-image-bytes.test-d.ts +108 -0
- package/src/__tests__/decode-image-bytes.test.ts +90 -0
- package/src/__tests__/decode-image-mime-owner.test.ts +2 -5
- package/src/__tests__/dynamic-texture-store.unit.test.ts +115 -0
- package/src/__tests__/errors-asset.unit.test.ts +92 -0
- package/src/__tests__/inline-pack-loaders.unit.test.ts +548 -0
- package/src/__tests__/instantiate-context.unit.test.ts +65 -0
- package/src/__tests__/instantiate-publication-fence.unit.test.ts +158 -0
- package/src/__tests__/instantiate.unit.test.ts +684 -0
- package/src/__tests__/loader-input-contract.unit.test.ts +89 -0
- package/src/__tests__/loader-registry.unit.test.ts +101 -0
- package/src/__tests__/material-dependency-invalidation.unit.test.ts +14 -0
- package/src/__tests__/material-generation-cache.unit.test.ts +46 -0
- package/src/__tests__/material-load-consumer.test-d.ts +42 -0
- package/src/__tests__/material-ready.integration.test.ts +492 -0
- package/src/__tests__/material-stale-generation.unit.test.ts +152 -0
- package/src/__tests__/mesh-bin-morph-v3.test.ts +22 -0
- package/src/__tests__/mesh-bin.unit.test.ts +152 -0
- package/src/__tests__/mipmap-generator.unit.test.ts +202 -0
- package/src/__tests__/pack-artifact-transcode-model-owner.test.ts +32 -0
- package/src/__tests__/pack-basis-load.integration.test.ts +168 -0
- package/src/__tests__/payload-validate.unit.test.ts +285 -0
- package/src/__tests__/plugin.test.ts +55 -0
- package/src/__tests__/registry-concurrency.bench.test.ts +40 -0
- package/src/__tests__/registry-failure-retry.integration.test.ts +69 -0
- package/src/__tests__/registry-load-graph.integration.test.ts +246 -0
- package/src/__tests__/registry-load-state.unit.test.ts +35 -0
- package/src/__tests__/registry-package-cache.unit.test.ts +53 -0
- package/src/__tests__/registry-public-ready.unit.test.ts +38 -0
- package/src/__tests__/resolve-asset-handle.unit.test.ts +238 -0
- package/src/__tests__/scene-handle-fields.unit.test.ts +129 -0
- package/src/__tests__/scene-payload.unit.test.ts +151 -0
- package/src/__tests__/scene-publication-fence.test.ts +129 -0
- package/src/__tests__/scriptable-pack-public-surface.test-d.ts +16 -0
- package/src/__tests__/static-rig.integration.test.ts +8 -0
- package/src/__tests__/validate-material.unit.test.ts +359 -0
- package/src/__tests__/video-pack-loader.integration.test.ts +83 -0
- package/src/aabb.ts +107 -0
- package/src/asset-registry.ts +2040 -0
- package/src/builtin-asset-registry.ts +164 -0
- package/src/catalog-source.ts +14 -70
- package/src/decode-image-bytes.ts +126 -0
- package/src/dynamic-texture-store.ts +234 -0
- package/src/errors/asset.ts +347 -0
- package/src/handles.ts +180 -0
- package/src/image-error.ts +45 -0
- package/src/index.ts +177 -19
- package/src/internal.ts +2 -0
- package/src/loader-registry.ts +157 -0
- package/src/loaders/inline-pack.ts +837 -0
- package/src/loaders/mesh-bin.ts +304 -0
- package/src/loaders/pack-artifact.ts +665 -0
- package/src/material/dependency-graph.ts +46 -0
- package/src/material/generation-cache.ts +177 -0
- package/src/material/inspection.ts +46 -0
- package/src/material/loader.ts +326 -0
- package/src/material/runtime-shader.ts +133 -0
- package/src/mesh-bin.ts +5 -0
- package/src/mipmap-generator.ts +450 -0
- package/src/payload-validate.ts +573 -0
- package/src/plugin.ts +48 -0
- package/src/registry/artifact-io.ts +259 -0
- package/src/registry/asset-evidence.ts +51 -0
- package/src/registry/catalog-state.ts +409 -0
- package/src/registry/catalog.ts +369 -0
- package/src/registry/instantiate.ts +1056 -0
- package/src/registry/load-by-guid.ts +1657 -0
- package/src/registry/load-state.ts +115 -0
- package/src/registry/runtime-projection.ts +63 -0
- package/src/registry/scene-publication-fence.ts +256 -0
- package/src/registry/validate-material.ts +386 -0
- package/src/resolve-asset-handle.ts +147 -0
- package/src/resolve-tileset-runtime.ts +72 -0
- package/src/scene-handle-fields.ts +221 -0
- package/src/scene-payload.ts +207 -0
- package/src/wire-default-loaders.ts +79 -0
- package/dist/__tests__/asset-graph-red.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-graph-red.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-kind.test.d.ts +0 -2
- package/dist/__tests__/asset-kind.test.d.ts.map +0 -1
- package/dist/__tests__/asset-registry-core.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-registry-core.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-registry-public-api.test-d.d.ts +0 -2
- package/dist/__tests__/asset-registry-public-api.test-d.d.ts.map +0 -1
- package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts +0 -2
- package/dist/__tests__/asset-runtime-core-lifecycle.integration.test.d.ts.map +0 -1
- package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts +0 -2
- package/dist/__tests__/asset-runtime-snapshot.unit.test.d.ts.map +0 -1
- package/dist/__tests__/catalog-session-red.unit.test.d.ts +0 -2
- package/dist/__tests__/catalog-session-red.unit.test.d.ts.map +0 -1
- package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts +0 -2
- package/dist/__tests__/registry-lifecycle-red.integration.test.d.ts.map +0 -1
- package/src/__tests__/asset-graph-red.integration.test.ts +0 -113
- package/src/__tests__/asset-registry-core.integration.test.ts +0 -161
- package/src/__tests__/asset-registry-public-api.test-d.ts +0 -31
- package/src/__tests__/asset-runtime-core-lifecycle.integration.test.ts +0 -79
- package/src/__tests__/asset-runtime-snapshot.unit.test.ts +0 -23
- package/src/__tests__/catalog-session-red.unit.test.ts +0 -276
- package/src/__tests__/registry-lifecycle-red.integration.test.ts +0 -80
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
// @forgeax/engine-assets-runtime - mipmap-generator.ts (T-M3-04).
|
|
2
|
+
//
|
|
3
|
+
// Independent-file home of the runtime mipmap auto-generation utility
|
|
4
|
+
// (plan-strategy section 2.6 D Open Q-5 selection (a)). Purposefully lives
|
|
5
|
+
// outside `asset-registry.ts` so the asset-registry stays single-purpose
|
|
6
|
+
// (charter proposition 5 producer / consumer split; architecture-principles
|
|
7
|
+
// pipeline isolation).
|
|
8
|
+
//
|
|
9
|
+
// Shader-module DI boundary (bug-20260518 D-1): the per-device pipeline
|
|
10
|
+
// cache builds its single ShaderModule via an injected
|
|
11
|
+
// `MipmapShaderModuleFactory`, NOT via `device.createShaderModule` -- the
|
|
12
|
+
// post-fix-f3 `RhiDevice` surface has no synchronous `createShaderModule`
|
|
13
|
+
// member, and the runtime mipmap utility must NOT reverse-import the
|
|
14
|
+
// rhi-webgpu shim package (charter F1 layering). The factory is wired
|
|
15
|
+
// once at `createRenderer.ts` from `pack.createShaderModule` and threaded
|
|
16
|
+
// through `AssetRegistry.configureGpuDevice` -> `generateMipmaps` ->
|
|
17
|
+
// `getOrCreateMipmapPipeline` -> `ensureDeviceCache`.
|
|
18
|
+
//
|
|
19
|
+
// SSOT three-source convergence (research F-1 + F-2):
|
|
20
|
+
// - webgpufundamentals importing-textures-mipmaps (formula + main loop)
|
|
21
|
+
// - toji web-texture-tool webgpu-mipmap-generator (oversized triangle WGSL)
|
|
22
|
+
// - greggman webgpu-utils generate-mipmap (WeakMap<GPUDevice> cache)
|
|
23
|
+
//
|
|
24
|
+
// Public surface (charter F2 minimal):
|
|
25
|
+
// - numMipLevels({ width, height }) -> number (formula SSOT)
|
|
26
|
+
// - getOrCreateMipmapPipeline(device, format) -> Result (cache lookup; per-format)
|
|
27
|
+
// - mipmapCacheSize(device) -> number (introspection)
|
|
28
|
+
// - generateMipmaps(device, texture, descriptor) -> Result<void>(per-mip render-pass blit-loop)
|
|
29
|
+
//
|
|
30
|
+
// Per-device cache structure (greggman SSOT):
|
|
31
|
+
// WeakMap<MipmapDevice, MipmapPipelineCache>
|
|
32
|
+
// where MipmapPipelineCache = {
|
|
33
|
+
// sampler: Sampler, // shared linear / linear / linear sampler (F-5)
|
|
34
|
+
// module: ShaderModule, // shared oversized-triangle vertex + sampling fragment WGSL
|
|
35
|
+
// layout: BindGroupLayout, // shared sampler @binding(0) + texture_2d<f32> @binding(1)
|
|
36
|
+
// pipelines: Map<GPUTextureFormat, RenderPipeline> // per-format pipeline (F-2 cache key)
|
|
37
|
+
// }
|
|
38
|
+
// Per-device isolation prevents pipeline leak across `createRenderer` calls
|
|
39
|
+
// (charter P5 consistent abstraction; greggman 36-44 anchor).
|
|
40
|
+
//
|
|
41
|
+
// Why this layout (not closure / not class):
|
|
42
|
+
// - `getOrCreateMipmapPipeline` is module-level for test surface (T-M3-01
|
|
43
|
+
// directly imports it; cache reference equality assertions need stable
|
|
44
|
+
// identity across calls).
|
|
45
|
+
// - `numMipLevels` is pure (no device argument); the WeakMap lookup only
|
|
46
|
+
// happens at pipeline-create time.
|
|
47
|
+
|
|
48
|
+
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Subset of `RhiDevice` consumed by the mipmap utility (charter F2: only
|
|
52
|
+
* the methods the algorithm actually invokes; the dawn integration tests
|
|
53
|
+
* pass a real `RhiDevice` and the unit cache tests pass a hand-rolled stub).
|
|
54
|
+
*
|
|
55
|
+
* Four entry-points (all synchronous Result):
|
|
56
|
+
* - createSampler (default linear / linear / linear)
|
|
57
|
+
* - createBindGroupLayout
|
|
58
|
+
* - createPipelineLayout
|
|
59
|
+
* - createRenderPipeline (per-format pipeline cache miss)
|
|
60
|
+
*
|
|
61
|
+
* Shader module is NOT an entry-point here; it is supplied by an injected
|
|
62
|
+
* `MipmapShaderModuleFactory` (bug-20260518 D-1) so the utility does not
|
|
63
|
+
* reach into the rhi-webgpu shim package from the runtime layer.
|
|
64
|
+
*
|
|
65
|
+
* Plus `createCommandEncoder` + `queue.submit` are added once the upload
|
|
66
|
+
* call-site (T-M3-05) wires generateMipmaps in; the cache-only public hooks
|
|
67
|
+
* exercised by T-M3-01 do not need them, so they live behind an extended
|
|
68
|
+
* MipmapDeviceForBlit alias.
|
|
69
|
+
*/
|
|
70
|
+
export type MipmapDevice = {
|
|
71
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
72
|
+
createSampler(desc?: any): Result<any, any>;
|
|
73
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
74
|
+
createBindGroupLayout(desc: any): Result<any, any>;
|
|
75
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
76
|
+
createPipelineLayout(desc: any): Result<any, any>;
|
|
77
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
78
|
+
createRenderPipeline(desc: any): Result<any, any>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Async shader-module factory injected through the call-chain
|
|
83
|
+
* `createRenderer.ts` -> `AssetRegistry.configureGpuDevice` ->
|
|
84
|
+
* `generateMipmaps` -> `getOrCreateMipmapPipeline` -> `ensureDeviceCache`
|
|
85
|
+
* (bug-20260518 D-1 / D-4).
|
|
86
|
+
*
|
|
87
|
+
* Signature mirrors the top-level async `createShaderModule(device, desc)`
|
|
88
|
+
* exposed by the rhi-webgpu shim (and the rhi-wgpu shim) so the
|
|
89
|
+
* call-site in createRenderer can pass `pack.createShaderModule` directly
|
|
90
|
+
* without a structural cast. The opaque module value is typed `unknown`
|
|
91
|
+
* here -- callers do not introspect it; they hand the value back to
|
|
92
|
+
* `createRenderPipeline` descriptors via the cached `MipmapPipelineCache`.
|
|
93
|
+
*/
|
|
94
|
+
export type MipmapShaderModuleFactory = (
|
|
95
|
+
device: MipmapDevice,
|
|
96
|
+
desc: { code: string; label?: string },
|
|
97
|
+
) => Promise<Result<unknown, RhiError>>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Per-device pipeline cache structure (research F-2 SSOT).
|
|
101
|
+
* Held under a module-private `WeakMap<MipmapDevice, MipmapPipelineCache>`.
|
|
102
|
+
*/
|
|
103
|
+
interface MipmapPipelineCache {
|
|
104
|
+
// biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU sampler
|
|
105
|
+
readonly sampler: any;
|
|
106
|
+
// biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU shader module
|
|
107
|
+
readonly module: any;
|
|
108
|
+
// biome-ignore lint/suspicious/noExplicitAny: cached opaque GPU bind-group layout
|
|
109
|
+
readonly layout: any;
|
|
110
|
+
// biome-ignore lint/suspicious/noExplicitAny: per-format render pipeline cache
|
|
111
|
+
readonly pipelines: Map<GPUTextureFormat, any>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Module-private cache (greggman 36-44 SSOT). WeakMap so that destroying a
|
|
115
|
+
// device naturally clears its cache (no manual cleanup hook needed).
|
|
116
|
+
const deviceCache: WeakMap<MipmapDevice, MipmapPipelineCache> = new WeakMap();
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Oversized-triangle WGSL (toji + greggman convergent template).
|
|
120
|
+
*
|
|
121
|
+
* Vertex stage emits a single triangle covering the entire NDC, and texCoord
|
|
122
|
+
* is computed from clip-space position via `pos * vec2(0.5, -0.5) +
|
|
123
|
+
* vec2(0.5)` (NDC -> UV with Y-flip in one fused MAD; saves a 6-vertex quad).
|
|
124
|
+
*
|
|
125
|
+
* Fragment stage samples the bound `texture_2d<f32>` with the linear /
|
|
126
|
+
* linear / linear sampler -- bilinear when the source mip is read by the
|
|
127
|
+
* destination mip, automatic sRGB decode / encode happens in the spec when
|
|
128
|
+
* source AND destination views are the SAME `*-srgb` format (research F-3).
|
|
129
|
+
*/
|
|
130
|
+
const MIPMAP_WGSL = `
|
|
131
|
+
struct VsOut {
|
|
132
|
+
@builtin(position) clip : vec4<f32>,
|
|
133
|
+
@location(0) uv : vec2<f32>,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
@vertex
|
|
137
|
+
fn vs_main(@builtin(vertex_index) vid : u32) -> VsOut {
|
|
138
|
+
// 3 vertices forming an oversized triangle: (-1,-1), (-1,3), (3,-1)
|
|
139
|
+
var positions = array<vec2<f32>, 3>(
|
|
140
|
+
vec2<f32>(-1.0, -1.0),
|
|
141
|
+
vec2<f32>(-1.0, 3.0),
|
|
142
|
+
vec2<f32>( 3.0, -1.0),
|
|
143
|
+
);
|
|
144
|
+
let pos = positions[vid];
|
|
145
|
+
var out : VsOut;
|
|
146
|
+
out.clip = vec4<f32>(pos, 0.0, 1.0);
|
|
147
|
+
// NDC -> UV with Y-flip fused into a single MAD.
|
|
148
|
+
out.uv = pos * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@group(0) @binding(0) var src_sampler : sampler;
|
|
153
|
+
@group(0) @binding(1) var src_texture : texture_2d<f32>;
|
|
154
|
+
|
|
155
|
+
@fragment
|
|
156
|
+
fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
157
|
+
return textureSample(src_texture, src_sampler, in.uv);
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Compute mip-level count for a 2D texture (research F-1 SSOT formula).
|
|
163
|
+
*
|
|
164
|
+
* Spec-anchor: WebGPU §3.6 texture-creation says `mipLevelCount` defaults
|
|
165
|
+
* to 1; the runtime utility opts AI users into a full chain via
|
|
166
|
+
* `floor(log2(max(width, height))) + 1` -- the same formula greggman /
|
|
167
|
+
* toji / webgpufundamentals all converge on.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* numMipLevels({ width: 256, height: 256 }) // 9
|
|
171
|
+
* numMipLevels({ width: 1, height: 1 }) // 1
|
|
172
|
+
*/
|
|
173
|
+
export function numMipLevels(size: { width: number; height: number }): number {
|
|
174
|
+
const max = Math.max(size.width, size.height);
|
|
175
|
+
if (max <= 1) return 1;
|
|
176
|
+
return Math.floor(Math.log2(max)) + 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Number of cached pipelines for the device (introspection hook used by
|
|
181
|
+
* T-M3-01 (c) cache-hit / (d) per-device-isolation tests; charter P3
|
|
182
|
+
* explicit failure: a cache size of 0 on a fresh device is a witnessed
|
|
183
|
+
* pre-condition rather than a swept-under hint).
|
|
184
|
+
*/
|
|
185
|
+
export function mipmapCacheSize(device: MipmapDevice): number {
|
|
186
|
+
const cache = deviceCache.get(device);
|
|
187
|
+
return cache?.pipelines.size ?? 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get the existing per-format render pipeline or build a fresh one.
|
|
192
|
+
*
|
|
193
|
+
* Per-device WeakMap + per-format `Map<GPUTextureFormat, RenderPipeline>`
|
|
194
|
+
* (greggman 36-44 + 119-122). The shared sampler / shader-module /
|
|
195
|
+
* bind-group-layout are constructed lazily on the first cache miss for a
|
|
196
|
+
* given device and reused across all formats on that device.
|
|
197
|
+
*/
|
|
198
|
+
export async function getOrCreateMipmapPipeline(
|
|
199
|
+
device: MipmapDevice,
|
|
200
|
+
format: GPUTextureFormat,
|
|
201
|
+
asyncCreateShaderModule: MipmapShaderModuleFactory,
|
|
202
|
+
// biome-ignore lint/suspicious/noExplicitAny: returns opaque GPU pipeline
|
|
203
|
+
): Promise<Result<any, unknown>> {
|
|
204
|
+
const cache = await ensureDeviceCache(device, asyncCreateShaderModule);
|
|
205
|
+
if (!cache.ok) return cache;
|
|
206
|
+
const existing = cache.value.pipelines.get(format);
|
|
207
|
+
if (existing !== undefined) {
|
|
208
|
+
return ok(existing);
|
|
209
|
+
}
|
|
210
|
+
const layoutRes = device.createPipelineLayout({
|
|
211
|
+
label: 'mipmap-pl',
|
|
212
|
+
bindGroupLayouts: [cache.value.layout],
|
|
213
|
+
});
|
|
214
|
+
if (!layoutRes.ok) return layoutRes;
|
|
215
|
+
const pipelineRes = device.createRenderPipeline({
|
|
216
|
+
label: `mipmap-pipeline-${format}`,
|
|
217
|
+
layout: layoutRes.value,
|
|
218
|
+
vertex: { module: cache.value.module, entryPoint: 'vs_main' },
|
|
219
|
+
fragment: {
|
|
220
|
+
module: cache.value.module,
|
|
221
|
+
entryPoint: 'fs_main',
|
|
222
|
+
targets: [{ format }],
|
|
223
|
+
},
|
|
224
|
+
primitive: { topology: 'triangle-list' },
|
|
225
|
+
});
|
|
226
|
+
if (!pipelineRes.ok) return pipelineRes;
|
|
227
|
+
cache.value.pipelines.set(format, pipelineRes.value);
|
|
228
|
+
return pipelineRes;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function ensureDeviceCache(
|
|
232
|
+
device: MipmapDevice,
|
|
233
|
+
asyncCreateShaderModule: MipmapShaderModuleFactory,
|
|
234
|
+
): Promise<Result<MipmapPipelineCache, RhiError>> {
|
|
235
|
+
const existing = deviceCache.get(device);
|
|
236
|
+
if (existing !== undefined) return ok(existing);
|
|
237
|
+
|
|
238
|
+
const moduleRes = await asyncCreateShaderModule(device, {
|
|
239
|
+
code: MIPMAP_WGSL,
|
|
240
|
+
label: 'mipmap-wgsl',
|
|
241
|
+
});
|
|
242
|
+
if (!moduleRes.ok) return moduleRes;
|
|
243
|
+
|
|
244
|
+
const samplerRes = device.createSampler({
|
|
245
|
+
magFilter: 'linear',
|
|
246
|
+
minFilter: 'linear',
|
|
247
|
+
mipmapFilter: 'linear',
|
|
248
|
+
});
|
|
249
|
+
if (!samplerRes.ok) return samplerRes;
|
|
250
|
+
|
|
251
|
+
// GPUShaderStage.FRAGMENT = 0x2 spec constant; embedded literally to keep
|
|
252
|
+
// the utility math-free (charter F2 minimal surface; spec value is stable).
|
|
253
|
+
const FRAGMENT_STAGE = 0x2;
|
|
254
|
+
const layoutRes = device.createBindGroupLayout({
|
|
255
|
+
label: 'mipmap-bgl',
|
|
256
|
+
entries: [
|
|
257
|
+
{ binding: 0, visibility: FRAGMENT_STAGE, sampler: { type: 'filtering' } },
|
|
258
|
+
{
|
|
259
|
+
binding: 1,
|
|
260
|
+
visibility: FRAGMENT_STAGE,
|
|
261
|
+
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
|
+
if (!layoutRes.ok) return layoutRes;
|
|
266
|
+
|
|
267
|
+
const cache: MipmapPipelineCache = {
|
|
268
|
+
sampler: samplerRes.value,
|
|
269
|
+
module: moduleRes.value,
|
|
270
|
+
layout: layoutRes.value,
|
|
271
|
+
pipelines: new Map(),
|
|
272
|
+
};
|
|
273
|
+
deviceCache.set(device, cache);
|
|
274
|
+
return ok(cache);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Extended device shape used by `generateMipmaps` (call-site T-M3-05).
|
|
279
|
+
* Adds command-encoder + bind-group + texture-view + queue surfaces beyond
|
|
280
|
+
* the cache-only `MipmapDevice` so the pipeline-cache unit tests still
|
|
281
|
+
* compile against the smaller surface.
|
|
282
|
+
*/
|
|
283
|
+
export type MipmapBlitDevice = MipmapDevice & {
|
|
284
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
285
|
+
createTexture(desc: any): Result<any, any>;
|
|
286
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
287
|
+
createBindGroup(desc: any): Result<any, any>;
|
|
288
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
289
|
+
createTextureView(texture: any, desc: any): Result<any, any>;
|
|
290
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
291
|
+
createCommandEncoder(desc?: any): Result<any, any>;
|
|
292
|
+
readonly queue: {
|
|
293
|
+
// biome-ignore lint/suspicious/noExplicitAny: shim accepts any descriptor
|
|
294
|
+
submit(buffers: ReadonlyArray<any>): Result<void, any>;
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
function recordMipmapBlit<E>(
|
|
299
|
+
device: MipmapBlitDevice,
|
|
300
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
|
|
301
|
+
texture: any,
|
|
302
|
+
levels: number,
|
|
303
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU pipeline handle
|
|
304
|
+
pipeline: any,
|
|
305
|
+
cache: MipmapPipelineCache,
|
|
306
|
+
): Result<void, E> {
|
|
307
|
+
const encoderRes = device.createCommandEncoder({ label: 'mipmap-encoder' });
|
|
308
|
+
if (!encoderRes.ok) return encoderRes;
|
|
309
|
+
const encoder = encoderRes.value;
|
|
310
|
+
|
|
311
|
+
for (let i = 1; i < levels; i++) {
|
|
312
|
+
const srcViewRes = device.createTextureView(texture, {
|
|
313
|
+
baseMipLevel: i - 1,
|
|
314
|
+
mipLevelCount: 1,
|
|
315
|
+
dimension: '2d',
|
|
316
|
+
});
|
|
317
|
+
if (!srcViewRes.ok) return srcViewRes;
|
|
318
|
+
|
|
319
|
+
const dstViewRes = device.createTextureView(texture, {
|
|
320
|
+
baseMipLevel: i,
|
|
321
|
+
mipLevelCount: 1,
|
|
322
|
+
dimension: '2d',
|
|
323
|
+
});
|
|
324
|
+
if (!dstViewRes.ok) return dstViewRes;
|
|
325
|
+
|
|
326
|
+
const bindGroupRes = device.createBindGroup({
|
|
327
|
+
label: `mipmap-bg-${i}`,
|
|
328
|
+
layout: cache.layout,
|
|
329
|
+
entries: [
|
|
330
|
+
{ binding: 0, resource: { kind: 'sampler', value: cache.sampler } },
|
|
331
|
+
{ binding: 1, resource: { kind: 'textureView', value: srcViewRes.value } },
|
|
332
|
+
],
|
|
333
|
+
});
|
|
334
|
+
if (!bindGroupRes.ok) return bindGroupRes;
|
|
335
|
+
|
|
336
|
+
const pass = encoder.beginRenderPass({
|
|
337
|
+
label: `mipmap-pass-${i}`,
|
|
338
|
+
colorAttachments: [
|
|
339
|
+
{
|
|
340
|
+
view: dstViewRes.value,
|
|
341
|
+
clearValue: { r: 0, g: 0, b: 0, a: 1 },
|
|
342
|
+
loadOp: 'clear',
|
|
343
|
+
storeOp: 'store',
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
});
|
|
347
|
+
pass.setPipeline(pipeline);
|
|
348
|
+
pass.setBindGroup(0, bindGroupRes.value);
|
|
349
|
+
pass.draw(3, 1, 0, 0);
|
|
350
|
+
pass.end();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const finishRes = encoder.finish();
|
|
354
|
+
if (!finishRes.ok) return finishRes;
|
|
355
|
+
return device.queue.submit([finishRes.value]);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Per-mip render-pass blit-loop (research F-1 SSOT main template).
|
|
360
|
+
*
|
|
361
|
+
* For mip levels 1..levels-1: source view = previous mip {baseMipLevel:i-1,
|
|
362
|
+
* mipLevelCount:1}; destination view = current mip {baseMipLevel:i,
|
|
363
|
+
* mipLevelCount:1, usage: RENDER_ATTACHMENT}; oversized-triangle pipeline
|
|
364
|
+
* draws 3 vertices, fragment shader samples source via linear sampler.
|
|
365
|
+
*
|
|
366
|
+
* Spec sRGB correctness (F-3): when format is `*-srgb`, both views inherit
|
|
367
|
+
* the sRGB encoding so hardware decode -> bilinear in linear -> encode back
|
|
368
|
+
* happens automatically; no special-casing required.
|
|
369
|
+
*
|
|
370
|
+
* `levels` defaults to `numMipLevels({ width, height })`. AI users that
|
|
371
|
+
* already created the texture with explicit `mipLevelCount` should pass the
|
|
372
|
+
* same value here.
|
|
373
|
+
*/
|
|
374
|
+
export async function generateMipmaps(
|
|
375
|
+
device: MipmapBlitDevice,
|
|
376
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
|
|
377
|
+
texture: any,
|
|
378
|
+
descriptor: {
|
|
379
|
+
readonly format: GPUTextureFormat;
|
|
380
|
+
readonly width: number;
|
|
381
|
+
readonly height: number;
|
|
382
|
+
readonly levels?: number;
|
|
383
|
+
},
|
|
384
|
+
asyncCreateShaderModule: MipmapShaderModuleFactory,
|
|
385
|
+
): Promise<Result<void, unknown>> {
|
|
386
|
+
const levels = descriptor.levels ?? numMipLevels(descriptor);
|
|
387
|
+
if (levels <= 1) return ok(undefined);
|
|
388
|
+
|
|
389
|
+
const pipelineRes = await getOrCreateMipmapPipeline(
|
|
390
|
+
device,
|
|
391
|
+
descriptor.format,
|
|
392
|
+
asyncCreateShaderModule,
|
|
393
|
+
);
|
|
394
|
+
if (!pipelineRes.ok) return pipelineRes;
|
|
395
|
+
const cache = deviceCache.get(device);
|
|
396
|
+
if (cache === undefined) {
|
|
397
|
+
return err({ code: 'webgpu-runtime-error', message: 'mipmap cache missing after create' });
|
|
398
|
+
}
|
|
399
|
+
return recordMipmapBlit<unknown>(device, texture, levels, pipelineRes.value, cache);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Synchronous sibling of `generateMipmaps` (feat-20260601-gpu-resource-store
|
|
404
|
+
* -extraction D-9). Identical per-mip blit loop, but reads the per-format
|
|
405
|
+
* pipeline + shared sampler / layout from the prewarmed device cache instead
|
|
406
|
+
* of building them. The pipeline MUST already be present (via a prior
|
|
407
|
+
* `getOrCreateMipmapPipeline` / `prewarmMipmapPipeline` async call) -- an
|
|
408
|
+
* un-prewarmed device or format returns a structured `RhiError` rather than
|
|
409
|
+
* awaiting a build, so the synchronous `draw(world)` frame contract is never
|
|
410
|
+
* broken. The async build itself does not affect the written bytes (same
|
|
411
|
+
* shader, same encoder sequence), so prewarm + sync blit reproduces the
|
|
412
|
+
* pre-extraction async `generateMipmaps` byte-for-byte (D-9 sub-contract 5).
|
|
413
|
+
*/
|
|
414
|
+
export function blitMipmapsSync(
|
|
415
|
+
device: MipmapBlitDevice,
|
|
416
|
+
// biome-ignore lint/suspicious/noExplicitAny: opaque GPU texture handle
|
|
417
|
+
texture: any,
|
|
418
|
+
descriptor: {
|
|
419
|
+
readonly format: GPUTextureFormat;
|
|
420
|
+
readonly width: number;
|
|
421
|
+
readonly height: number;
|
|
422
|
+
readonly levels?: number;
|
|
423
|
+
},
|
|
424
|
+
): Result<void, RhiError> {
|
|
425
|
+
const levels = descriptor.levels ?? numMipLevels(descriptor);
|
|
426
|
+
if (levels <= 1) return ok(undefined);
|
|
427
|
+
|
|
428
|
+
const cache = deviceCache.get(device);
|
|
429
|
+
if (cache === undefined) {
|
|
430
|
+
return err(
|
|
431
|
+
new RhiError({
|
|
432
|
+
code: 'rhi-not-available',
|
|
433
|
+
expected: 'mipmap pipeline cache prewarmed for this device before blitMipmapsSync',
|
|
434
|
+
hint: 'call prewarmMipmapPipeline(device, formats) at renderer.ready before the synchronous record-stage mipmap blit',
|
|
435
|
+
}),
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
const pipeline = cache.pipelines.get(descriptor.format);
|
|
439
|
+
if (pipeline === undefined) {
|
|
440
|
+
return err(
|
|
441
|
+
new RhiError({
|
|
442
|
+
code: 'rhi-not-available',
|
|
443
|
+
expected: `mipmap pipeline for format ${descriptor.format} prewarmed`,
|
|
444
|
+
hint: `format ${descriptor.format} was not prewarmed; add it to the prewarmMipmapPipeline format list at renderer.ready`,
|
|
445
|
+
}),
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return recordMipmapBlit<RhiError>(device, texture, levels, pipeline, cache);
|
|
450
|
+
}
|