Finished up most documentation

This commit is contained in:
2026-09-10 21:11:07 -07:00
parent 2cfa0ca690
commit 8355743ada
14 changed files with 1871 additions and 868 deletions

View File

@@ -231,3 +231,114 @@ test('reduced-motion opening keeps the existing 70 ms transition duration', (t)
step(1)
assert.equal(pack.state, 'stackReady')
})
test('GPU preparation uploads unique textures and compiles hidden fronts and both edge looks', async (t) => {
const { pack, canvas } = setupPack(t)
pack.setActive(false)
const scene = new THREE.Scene()
scene.environment = pack.options.environment
const backTexture = new THREE.Texture()
pack.options.backMaterial.map = backTexture
const uploaded = []
const compiledEdges = []
let reveals = 0
canvas.addEventListener('packreveal', () => reveals++)
const before = geometryHash(pack.wrapper)
let finishCompile
const firstCompile = new Promise((resolve) => { finishCompile = resolve })
const renderer = {
initTexture(texture) { uploaded.push(texture) },
async compileAsync(root, camera, targetScene) {
assert.equal(root, pack.root)
assert.equal(camera, pack.camera)
assert.equal(targetScene, scene)
assert.equal(root.visible, false)
assert.equal(pack.state, 'sealed')
assert.equal(pack.cards.some((card) => card.front.visible), false)
assert.ok(pack.cards.every((card) => root.getObjectById(card.front.id)))
compiledEdges.push(pack.cards.map((card) => card.edge.clearcoat))
if (compiledEdges.length === 1) await firstCompile
},
}
let ready = false
const preparation = pack.prepareGPU(renderer, scene).then(() => { ready = true })
await Promise.resolve()
assert.equal(ready, false)
finishCompile()
await preparation
assert.equal(uploaded.length, new Set(uploaded).size)
assert.equal(uploaded.length, 9, '4 artwork/mask textures, normal, environment, back and 2 wrapper maps')
for (const { artwork, mask } of Object.values(pack.options.textures)) {
assert.ok(uploaded.includes(artwork))
assert.ok(uploaded.includes(mask))
}
assert.ok(uploaded.includes(backTexture))
assert.ok(uploaded.includes(pack.options.normalMap))
assert.ok(uploaded.includes(scene.environment))
assert.deepEqual(compiledEdges, [[0.02, 0.02, 0.02], [0, 0.02, 0.18]])
assert.deepEqual(pack.cards.map((card) => card.edge.clearcoat), [0.02, 0.02, 0.02])
assert.equal(geometryHash(pack.wrapper), before)
assert.equal(reveals, 0)
})
test('failed reveal-material warmup restores neutral edges and can be retried', async (t) => {
const { pack } = setupPack(t)
pack.setActive(false)
let compilations = 0
const failure = new Error('GPU compilation failed')
const renderer = {
initTexture() {},
async compileAsync() {
if (++compilations === 2) throw failure
},
}
const scene = new THREE.Scene()
await assert.rejects(pack.prepareGPU(renderer, scene), (error) => error === failure)
assert.deepEqual(pack.cards.map((card) => card.edge.clearcoat), [0.02, 0.02, 0.02])
assert.equal(pack.root.visible, false)
assert.equal(pack.state, 'sealed')
await pack.prepareGPU(renderer, scene)
assert.equal(compilations, 4)
})
test('GPU upload errors propagate and preparation cannot mutate an active pack', async (t) => {
const { pack } = setupPack(t)
const failure = new Error('GPU upload failed')
let uploads = 0
const renderer = {
initTexture() { uploads++; throw failure },
async compileAsync() { assert.fail('Compilation must not run after upload failure') },
}
const scene = new THREE.Scene()
await assert.rejects(pack.prepareGPU(renderer, scene), /before activating/)
assert.equal(uploads, 0)
pack.setActive(false)
await assert.rejects(pack.prepareGPU(renderer, scene), (error) => error === failure)
})
test('pack cleanup disposes owned resources without disposing shared or caller-owned textures', (t) => {
const { pack } = setupPack(t)
const scene = new THREE.Scene()
scene.add(pack.root)
const shared = [
pack.options.backMaterial, pack.options.normalMap, pack.options.environment,
...Object.values(pack.options.textures).flatMap(({ artwork, mask }) => [artwork, mask]),
]
const sharedSpies = shared.map((resource) => t.mock.method(resource, 'dispose'))
const geometries = new Set()
const materials = new Set()
pack.root.traverse((object) => {
if (!(object instanceof THREE.Mesh)) return
geometries.add(object.geometry)
materials.add(object.material)
})
materials.delete(pack.options.backMaterial)
const resources = [...geometries, ...materials,
...new Set(pack.wrapper.root.children.map((mesh) => mesh.material.map))]
const ownedSpies = resources.map((resource) => t.mock.method(resource, 'dispose'))
pack.dispose()
assert.equal(pack.root.parent, null)
assert.equal(pack.root.visible, false)
assert.ok(ownedSpies.every((spy) => spy.mock.callCount() === 1))
assert.ok(sharedSpies.every((spy) => spy.mock.callCount() === 0))
})