@voluma/vlam 0.2.3 → 0.3.0
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 +3 -2
- package/dist/budget-governor.d.ts +4 -4
- package/dist/camera-budget-governor.d.ts +11 -11
- package/dist/chunk-cache-budget.d.ts +5 -5
- package/dist/chunk-fetch-scheduler.d.ts +4 -4
- package/dist/chunk-loader-DlNwggfG.js +1859 -0
- package/dist/chunk-loader-DlNwggfG.js.map +1 -0
- package/dist/chunk-loader.d.ts +2 -2
- package/dist/compute-sorter.d.ts +1 -1
- package/dist/effects.d.ts +26 -16
- package/dist/effects.js +199 -165
- package/dist/effects.js.map +1 -1
- package/dist/formats/ksplat/index.d.ts +1 -1
- package/dist/formats/lcc/collision-mesh.d.ts +2 -2
- package/dist/formats/lcc.js +17 -16
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/ply/index.d.ts +1 -1
- package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
- package/dist/formats/ply.js +1 -1
- package/dist/formats/ply.js.map +1 -1
- package/dist/formats/rad/index.d.ts +1 -1
- package/dist/formats/rad/parse-rad.d.ts +1 -1
- package/dist/formats/rad/rad.d.ts +2 -2
- package/dist/formats/rad.js +3 -3
- package/dist/formats/rad.js.map +1 -1
- package/dist/formats/sog/index.d.ts +1 -1
- package/dist/formats/sog/parse-sog.d.ts +4 -4
- package/dist/formats/sog.js.map +1 -1
- package/dist/formats/splat/index.d.ts +1 -1
- package/dist/formats/spz/index.d.ts +1 -1
- package/dist/index.d.ts +16 -25
- package/dist/index.js +253 -9047
- package/dist/index.js.map +1 -1
- package/dist/{load-scene.d.ts → load-splat-data.d.ts} +15 -13
- package/dist/loaders.d.ts +15 -0
- package/dist/loaders.js +26 -0
- package/dist/loaders.js.map +1 -0
- package/dist/loading-wLo8vRbA.js.map +1 -1
- package/dist/loading.d.ts +8 -8
- package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
- package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
- package/dist/lod-scheduler.d.ts +1 -1
- package/dist/lod-source.d.ts +5 -5
- package/dist/{splat-scene.d.ts → merged-splat-mesh.d.ts} +14 -15
- package/dist/orientation.d.ts +7 -7
- package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
- package/dist/{ply-header-BwgExCn-.js.map → ply-header-B9WllbPg.js.map} +1 -1
- package/dist/radix-sorter-R0FCSqKu.js +178 -0
- package/dist/radix-sorter-R0FCSqKu.js.map +1 -0
- package/dist/relighting-CSQF3UcL.js +969 -0
- package/dist/relighting-CSQF3UcL.js.map +1 -0
- package/dist/relighting.d.ts +1 -1
- package/dist/selection.d.ts +13 -0
- package/dist/selection.js +137 -0
- package/dist/selection.js.map +1 -0
- package/dist/source-transform.d.ts +4 -4
- package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
- package/dist/{splat-budget-DXew_86c.js.map → splat-budget-DuVZspPQ.js.map} +1 -1
- package/dist/splat-budget.d.ts +1 -1
- package/dist/splat-data-BZM5dhhx.js.map +1 -1
- package/dist/splat-data.d.ts +4 -4
- package/dist/splat-mesh-aQdcWA6v.js +1942 -0
- package/dist/splat-mesh-aQdcWA6v.js.map +1 -0
- package/dist/splat-mesh-material.d.ts +2 -2
- package/dist/splat-mesh-types-BZIko-_9.js +18 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
- package/dist/splat-mesh-types.d.ts +14 -8
- package/dist/splat-mesh.d.ts +4 -4
- package/dist/splat-modifier.d.ts +5 -5
- package/dist/splat-partition.d.ts +1 -1
- package/dist/static-lod-entry.d.ts +12 -0
- package/dist/static-lod-splat-mesh.d.ts +2 -2
- package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
- package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
- package/dist/static-lod.js +148 -0
- package/dist/static-lod.js.map +1 -0
- package/dist/streamed-splat-mesh.d.ts +21 -27
- package/dist/streaming.d.ts +23 -0
- package/dist/streaming.js +2877 -0
- package/dist/streaming.js.map +1 -0
- package/dist/{unified-splat-renderer.d.ts → unified-splat-mesh.d.ts} +10 -10
- package/dist/unified-work-buffer.d.ts +66 -0
- package/dist/unified.d.ts +11 -0
- package/dist/unified.js +738 -0
- package/dist/unified.js.map +1 -0
- package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
- package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
- package/dist/webgpu-limits.d.ts +0 -59
- package/dist/work-buffer-gather.d.ts +1 -1
- package/package.json +24 -4
- package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* transparent draw calls, each depth-sorted only within itself, so where the
|
|
7
7
|
* clouds overlap one is painted wholesale in front of the other ("pasted over")
|
|
8
8
|
* - the same artifact PlayCanvas's unified rendering and Spark's accumulator
|
|
9
|
-
* exist to fix. `
|
|
9
|
+
* exist to fix. `MergedSplatMesh` concatenates every cloud into **one pool** and
|
|
10
10
|
* runs **one global depth sort**, so splats from different clouds interleave
|
|
11
11
|
* correctly, from any angle.
|
|
12
12
|
*
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* is a handful of uniform writes - a source can be dragged or animated every
|
|
18
18
|
* frame at full rate, at any splat count, with no data re-upload.
|
|
19
19
|
*
|
|
20
|
-
* The placement runs **before** the modifier stack, so
|
|
20
|
+
* The placement runs **before** the modifier stack, so an application effect sees each
|
|
21
21
|
* splat where it visually is: an SDF light shape spanning two sources paints
|
|
22
22
|
* one continuous shape, and a reveal sweeps them together. A modifier that
|
|
23
23
|
* wants to travel *with* a moved source reads `ctx.sourceCenter` instead of
|
|
@@ -30,27 +30,26 @@
|
|
|
30
30
|
* Limitations, all inherited from the dynamic pool: sources may not carry
|
|
31
31
|
* palette (SOG `shN`) view-dependent color - per-file palettes cannot be merged
|
|
32
32
|
* - but DC color and per-splat (LCC) SH are fine; all sources share one
|
|
33
|
-
* antialias setting (from the
|
|
33
|
+
* antialias setting (from the mesh options).
|
|
34
34
|
*
|
|
35
|
-
* Sources are
|
|
36
|
-
* {@link
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* the static sources. Streamed sources are ROADMAP M15.4.
|
|
35
|
+
* Sources are fully decoded {@link SplatData} copied once into the pool.
|
|
36
|
+
* {@link StreamedSplatMesh} swaps its resident LOD cut every frame, so it cannot
|
|
37
|
+
* be a merged source. Compose streamed meshes with fully loaded meshes through
|
|
38
|
+
* {@link UnifiedSplatMesh} instead.
|
|
40
39
|
*/
|
|
41
40
|
import * as THREE from 'three/webgpu';
|
|
42
41
|
import { SplatMesh, type SplatMeshOptions } from './splat-mesh';
|
|
43
42
|
import type { SplatData } from './splat-data';
|
|
44
43
|
import { type SplatOrientation } from './orientation';
|
|
45
|
-
/** Construction options for a {@link
|
|
46
|
-
export interface
|
|
44
|
+
/** Construction options for a {@link MergedSplatMesh}. */
|
|
45
|
+
export interface MergedSplatMeshOptions extends SplatMeshOptions {
|
|
47
46
|
/** Total pool size in splats - must cover the sum of every source added. */
|
|
48
47
|
capacity: number;
|
|
49
|
-
/** Maximum number of sources (default {@link
|
|
48
|
+
/** Maximum number of sources (default {@link MAX_MERGED_SPLAT_SOURCES}). */
|
|
50
49
|
maxSources?: number;
|
|
51
50
|
}
|
|
52
51
|
/** Per-source placement options. */
|
|
53
|
-
export interface
|
|
52
|
+
export interface MergedSplatSourceOptions {
|
|
54
53
|
/**
|
|
55
54
|
* Y-up normalization for this source's data frame, like {@link SplatMesh}'s
|
|
56
55
|
* `orientation`. `'y-up'` (default) applies the format's stand-up correction
|
|
@@ -64,13 +63,13 @@ export interface AddSourceOptions {
|
|
|
64
63
|
*
|
|
65
64
|
* @experimental May change in a minor release.
|
|
66
65
|
*/
|
|
67
|
-
export declare class
|
|
66
|
+
export declare class MergedSplatMesh extends SplatMesh {
|
|
68
67
|
private readonly matrices;
|
|
69
68
|
private readonly maxSources;
|
|
70
69
|
/** Indexed by source id; holes are left by {@link removeSource}. */
|
|
71
70
|
private readonly sources;
|
|
72
71
|
private nextId;
|
|
73
|
-
constructor(options:
|
|
72
|
+
constructor(options: MergedSplatMeshOptions);
|
|
74
73
|
/** Number of live sources. */
|
|
75
74
|
get sourceCount(): number;
|
|
76
75
|
/**
|
|
@@ -81,7 +80,7 @@ export declare class SplatScene extends SplatMesh {
|
|
|
81
80
|
*
|
|
82
81
|
* @throws if the pool cannot fit the cloud, or the source limit is reached.
|
|
83
82
|
*/
|
|
84
|
-
addSource(data: SplatData, placement?: THREE.Matrix4, options?:
|
|
83
|
+
addSource(data: SplatData, placement?: THREE.Matrix4, options?: MergedSplatSourceOptions): number;
|
|
85
84
|
/**
|
|
86
85
|
* Re-places a source. Cheap by design: only the shared matrix uniform and the
|
|
87
86
|
* sort bound change, so it is safe to call every frame while dragging.
|
package/dist/orientation.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as THREE from 'three/webgpu';
|
|
2
2
|
/**
|
|
3
|
-
* How a loaded
|
|
3
|
+
* How a loaded capture is oriented into the three.js Y-up world.
|
|
4
4
|
*
|
|
5
5
|
* - `'y-up'` (default): VLAM normalizes every known format to three.js Y-up -
|
|
6
6
|
* the OpenCV-frame formats (PLY/`.splat`/`.ksplat`/SOG/`.rad`) are flipped
|
|
7
7
|
* 180° about X, SPZ is already Y-up, and LCC carries its own Z-up→Y-up matrix.
|
|
8
8
|
* "Drop a file and it stands up", matching engine viewers (PlayCanvas, Babylon).
|
|
9
|
-
* - `'source'`:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* - `'source'`: keep the native/source coordinate frame. No cosmetic 180°-X flip.
|
|
10
|
+
* An application that manages orientation itself (for example swapping viewers
|
|
11
|
+
* while keeping one stored per-capture rotation) uses this.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* LCC's Z-up→Y-up matrix is format semantics, not a cosmetic default: it is
|
|
14
|
+
* applied in **both** modes (Spark applies it too). Only the 180°-X flip for
|
|
15
15
|
* the Y-down formats is gated by this option.
|
|
16
16
|
*/
|
|
17
17
|
export type SplatOrientation = 'y-up' | 'source';
|
|
18
18
|
/**
|
|
19
19
|
* Formats {@link yUpTransformForFormat} understands: the self-contained scene
|
|
20
|
-
* formats ({@link SplatData.
|
|
20
|
+
* formats ({@link SplatData.format}) plus the streamed ones.
|
|
21
21
|
*/
|
|
22
22
|
export type OrientableFormat = 'ply' | 'splat' | 'ksplat' | 'sog' | 'spz' | 'rad' | 'streamed-sog' | 'lcc' | 'lcc2';
|
|
23
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ply-header-BwgExCn-.js","sources":["../src/lib/ply-header.ts"],"sourcesContent":["/**\n * Shared PLY header reader.\n *\n * PLY is a container, not a format: the same `.ply` extension carries the\n * uncompressed \"INRIA flavor\" 3DGS export (see `parse-splat-ply.ts`) and\n * SuperSplat/PlayCanvas's compressed flavor (`parse-compressed-ply.ts`).\n * Both need the same thing first - the element list, their property layouts,\n * and where each element's records start - so that lives here.\n */\n\n/** Byte size of each scalar type PLY can declare. */\nconst PROPERTY_SIZES: Record<string, number> = {\n float: 4,\n float32: 4,\n double: 8,\n float64: 8,\n int: 4,\n int32: 4,\n uint: 4,\n uint32: 4,\n short: 2,\n int16: 2,\n ushort: 2,\n uint16: 2,\n char: 1,\n int8: 1,\n uchar: 1,\n uint8: 1,\n};\n\n/** One scalar property of an element. */\nexport interface PlyProperty {\n readonly type: string;\n /** Byte offset within one record of the element. */\n readonly offset: number;\n}\n\n/**\n * One list property (`property list <countType> <itemType> <name>`), such as a\n * mesh's `face` indices. Only the types are recorded - the data is variable-size\n * and must be walked record by record (see `parse-mesh-ply.ts`).\n */\nexport interface PlyListProperty {\n /** Type of the per-record item count, e.g. `uchar`. */\n readonly countType: string;\n /** Type of each item, e.g. `int`. */\n readonly itemType: string;\n}\n\n/** One PLY element (`element <name> <count>`) and its record layout. */\nexport interface PlyElement {\n readonly name: string;\n readonly count: number;\n readonly properties: ReadonlyMap<string, PlyProperty>;\n /** Property names in declaration order. */\n readonly propertyNames: readonly string[];\n /** List properties by name; empty unless {@link hasListProperty}. */\n readonly listProperties: ReadonlyMap<string, PlyListProperty>;\n /**\n * Bytes per record - meaningless when {@link hasListProperty}, since a list\n * makes each record's size depend on its contents.\n */\n readonly stride: number;\n /**\n * Byte offset of this element's first record within the file, or -1 when a\n * preceding element has a variable-size record and no arithmetic can find it.\n */\n readonly offset: number;\n /** A list property makes this element's records variable-size. */\n readonly hasListProperty: boolean;\n}\n\n/** A parsed PLY header. */\nexport interface PlyHeader {\n readonly elements: readonly PlyElement[];\n /** The element of that name, or undefined. */\n element(name: string): PlyElement | undefined;\n}\n\n/**\n * Reads a binary little-endian PLY header and resolves every element's byte\n * offset by walking the elements in declaration order.\n *\n * Whether the declared records actually fit in the buffer is deliberately not\n * checked here - call {@link assertPlyElementFits} once the caller has\n * validated the properties it needs, so a wrong property type is reported as\n * such rather than as the truncation it happens to imply.\n *\n * @throws {Error} if the file is not a binary little-endian PLY, or declares a\n * property type this reader cannot size.\n */\nexport function parsePlyHeader(buffer: ArrayBuffer): PlyHeader {\n const headerBytes = new Uint8Array(buffer, 0, Math.min(buffer.byteLength, 64 * 1024));\n const headerText = new TextDecoder('ascii').decode(headerBytes);\n const endMatch = /end_header\\r?\\n/.exec(headerText);\n if (!/^ply\\r?\\n/.test(headerText)) {\n throw new Error('Not a PLY file (missing \"ply\" magic).');\n }\n if (!endMatch) {\n throw new Error(\n 'Unsupported PLY file: no \"end_header\" within the first 64 KiB - the header exceeds the ' +\n 'supported window, or the file is truncated.',\n );\n }\n if (!headerText.includes('format binary_little_endian 1.0')) {\n throw new Error('Unsupported PLY format; expected binary_little_endian 1.0.');\n }\n\n interface Building {\n name: string;\n count: number;\n properties: Map<string, PlyProperty>;\n propertyNames: string[];\n listProperties: Map<string, PlyListProperty>;\n stride: number;\n hasListProperty: boolean;\n }\n const building: Building[] = [];\n\n for (const line of headerText.slice(0, endMatch.index).split('\\n')) {\n const tokens = line.trim().split(/\\s+/);\n if (tokens[0] === 'element') {\n const name = tokens[1];\n const count = Number(tokens[2]);\n if (name === undefined) throw new Error(`Malformed PLY element: \"${line.trim()}\"`);\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`Invalid PLY ${name} count: ${tokens[2]}.`);\n }\n building.push({\n name,\n count,\n properties: new Map(),\n propertyNames: [],\n listProperties: new Map(),\n stride: 0,\n hasListProperty: false,\n });\n } else if (tokens[0] === 'property') {\n const element = building[building.length - 1];\n if (!element) throw new Error(`PLY property declared before any element: \"${line.trim()}\"`);\n if (tokens[1] === 'list') {\n // A list makes this element's records variable-size. That is fine for\n // the element itself (a trailing `face` list is common) - it only\n // costs later elements their computable offset. The types are recorded\n // so a reader that does want the data can walk it record by record.\n const countType = tokens[2] ?? '';\n const itemType = tokens[3] ?? '';\n const listName = tokens[4];\n if (\n PROPERTY_SIZES[countType] === undefined ||\n PROPERTY_SIZES[itemType] === undefined ||\n listName === undefined\n ) {\n throw new Error(`Unsupported PLY property: \"${line.trim()}\"`);\n }\n if (element.properties.has(listName) || element.listProperties.has(listName)) {\n throw new Error(`Duplicate PLY property \"${listName}\" in element \"${element.name}\".`);\n }\n element.listProperties.set(listName, { countType, itemType });\n element.hasListProperty = true;\n continue;\n }\n const type = tokens[1] ?? '';\n const size = PROPERTY_SIZES[type];\n const name = tokens[2];\n if (size === undefined || name === undefined) {\n throw new Error(`Unsupported PLY property: \"${line.trim()}\"`);\n }\n if (element.properties.has(name) || element.listProperties.has(name)) {\n // A duplicate name would let the later offset overwrite the earlier\n // one while the stride counts both - every read after it lands on\n // the wrong bytes.\n throw new Error(`Duplicate PLY property \"${name}\" in element \"${element.name}\".`);\n }\n element.properties.set(name, { type, offset: element.stride });\n element.propertyNames.push(name);\n element.stride += size;\n }\n }\n\n // Records are laid out back to back in element order, starting right after\n // the header - so each element begins where the previous one ended, until a\n // variable-size element makes the rest unreachable by arithmetic.\n let offset = endMatch.index + endMatch[0].length;\n let resolved = true;\n const elements: PlyElement[] = [];\n for (const element of building) {\n elements.push({\n name: element.name,\n count: element.count,\n properties: element.properties,\n propertyNames: element.propertyNames,\n listProperties: element.listProperties,\n stride: element.stride,\n offset: resolved ? offset : -1,\n hasListProperty: element.hasListProperty,\n });\n if (element.hasListProperty) resolved = false;\n else offset += element.count * element.stride;\n }\n\n return {\n elements,\n element: (name: string) => elements.find((candidate) => candidate.name === name),\n };\n}\n\n/**\n * Asserts an element's records are actually present in a file of `totalBytes`.\n *\n * Takes a size rather than a buffer so it works for a streamed parse, which\n * only ever holds a window of the file.\n *\n * @throws {Error} if the element sits behind a variable-size one, or its\n * records run past the end of the file.\n */\nexport function assertPlyElementFits(element: PlyElement, totalBytes: number): void {\n if (element.offset < 0) {\n throw new Error(\n `Unsupported PLY layout: element \"${element.name}\" follows a list property, so its ` +\n 'position cannot be determined.',\n );\n }\n const end = element.offset + element.count * element.stride;\n if (end > totalBytes) {\n throw new Error(\n `Truncated PLY file: element \"${element.name}\" needs ${end} bytes but the file is ` +\n `${totalBytes}.`,\n );\n }\n}\n\n/**\n * Byte size of a PLY scalar type.\n *\n * @throws {Error} if the type is not one PLY can declare.\n */\nexport function plyTypeSize(type: string): number {\n const size = PROPERTY_SIZES[type];\n if (size === undefined) throw new Error(`Unsupported PLY property type: \"${type}\".`);\n return size;\n}\n\n/** The byte offset of `property` within `element`'s record. */\nexport function plyPropertyOffset(element: PlyElement, property: string): number {\n const found = element.properties.get(property);\n if (!found) {\n throw new Error(`PLY element \"${element.name}\" is missing property \"${property}\".`);\n }\n return found.offset;\n}\n\n/** Asserts every named property exists on `element` with the given type. */\nexport function assertPlyPropertyTypes(\n element: PlyElement,\n properties: readonly string[],\n types: readonly string[],\n what: string,\n): void {\n for (const name of properties) {\n const property = element.properties.get(name);\n if (!property) {\n throw new Error(`${what}: missing property \"${name}\".`);\n }\n if (!types.includes(property.type)) {\n throw new Error(\n `Unsupported PLY property type for \"${name}\": expected ${types.join(' or ')}, ` +\n `got ${property.type}.`,\n );\n }\n }\n}\n"],"names":["PROPERTY_SIZES","parsePlyHeader","buffer","headerBytes","headerText","endMatch","building","line","tokens","name","count","element","countType","itemType","listName","type","size","offset","resolved","elements","candidate","assertPlyElementFits","totalBytes","end","plyTypeSize","plyPropertyOffset","property","found","assertPlyPropertyTypes","properties","types","what"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ply-header-B9WllbPg.js","sources":["../src/lib/ply-header.ts"],"sourcesContent":["/**\n * Shared PLY header reader.\n *\n * PLY is a container, not a format: the same `.ply` extension carries the\n * uncompressed \"INRIA flavor\" 3DGS export (see `parse-splat-ply.ts`) and\n * SuperSplat/PlayCanvas's compressed flavor (`parse-compressed-ply.ts`).\n * Both need the same thing first - the element list, their property layouts,\n * and where each element's records start - so that lives here.\n */\n\n/** Byte size of each scalar type PLY can declare. */\nconst PROPERTY_SIZES: Record<string, number> = {\n float: 4,\n float32: 4,\n double: 8,\n float64: 8,\n int: 4,\n int32: 4,\n uint: 4,\n uint32: 4,\n short: 2,\n int16: 2,\n ushort: 2,\n uint16: 2,\n char: 1,\n int8: 1,\n uchar: 1,\n uint8: 1,\n};\n\n/** One scalar property of an element. */\nexport interface PlyProperty {\n readonly type: string;\n /** Byte offset within one record of the element. */\n readonly offset: number;\n}\n\n/**\n * One list property (`property list <countType> <itemType> <name>`), such as a\n * mesh's `face` indices. Only the types are recorded - the data is variable-size\n * and must be walked record by record (see `parse-mesh-ply.ts`).\n */\nexport interface PlyListProperty {\n /** Type of the per-record item count, e.g. `uchar`. */\n readonly countType: string;\n /** Type of each item, e.g. `int`. */\n readonly itemType: string;\n}\n\n/** One PLY element (`element <name> <count>`) and its record layout. */\nexport interface PlyElement {\n readonly name: string;\n readonly count: number;\n readonly properties: ReadonlyMap<string, PlyProperty>;\n /** Property names in declaration order. */\n readonly propertyNames: readonly string[];\n /** List properties by name; empty unless {@link hasListProperty}. */\n readonly listProperties: ReadonlyMap<string, PlyListProperty>;\n /**\n * Bytes per record - meaningless when {@link hasListProperty}, since a list\n * makes each record's size depend on its contents.\n */\n readonly stride: number;\n /**\n * Byte offset of this element's first record within the file, or -1 when a\n * preceding element has a variable-size record and no arithmetic can find it.\n */\n readonly offset: number;\n /** A list property makes this element's records variable-size. */\n readonly hasListProperty: boolean;\n}\n\n/** A parsed PLY header. */\nexport interface PlyHeader {\n readonly elements: readonly PlyElement[];\n /** The element of that name, or undefined. */\n element(name: string): PlyElement | undefined;\n}\n\n/**\n * Reads a binary little-endian PLY header and resolves every element's byte\n * offset by walking the elements in declaration order.\n *\n * Whether the declared records actually fit in the buffer is deliberately not\n * checked here - call {@link assertPlyElementFits} once the caller has\n * validated the properties it needs, so a wrong property type is reported as\n * such rather than as the truncation it happens to imply.\n *\n * @throws {Error} if the file is not a binary little-endian PLY, or declares a\n * property type this reader cannot size.\n */\nexport function parsePlyHeader(buffer: ArrayBuffer): PlyHeader {\n const headerBytes = new Uint8Array(buffer, 0, Math.min(buffer.byteLength, 64 * 1024));\n const headerText = new TextDecoder('ascii').decode(headerBytes);\n const endMatch = /end_header\\r?\\n/.exec(headerText);\n if (!/^ply\\r?\\n/.test(headerText)) {\n throw new Error('Not a PLY file (missing \"ply\" magic).');\n }\n if (!endMatch) {\n throw new Error(\n 'Unsupported PLY file: no \"end_header\" within the first 64 KiB - the header exceeds the ' +\n 'supported window, or the file is truncated.',\n );\n }\n if (!headerText.includes('format binary_little_endian 1.0')) {\n throw new Error('Unsupported PLY format; expected binary_little_endian 1.0.');\n }\n\n interface Building {\n name: string;\n count: number;\n properties: Map<string, PlyProperty>;\n propertyNames: string[];\n listProperties: Map<string, PlyListProperty>;\n stride: number;\n hasListProperty: boolean;\n }\n const building: Building[] = [];\n\n for (const line of headerText.slice(0, endMatch.index).split('\\n')) {\n const tokens = line.trim().split(/\\s+/);\n if (tokens[0] === 'element') {\n const name = tokens[1];\n const count = Number(tokens[2]);\n if (name === undefined) throw new Error(`Malformed PLY element: \"${line.trim()}\"`);\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`Invalid PLY ${name} count: ${tokens[2]}.`);\n }\n building.push({\n name,\n count,\n properties: new Map(),\n propertyNames: [],\n listProperties: new Map(),\n stride: 0,\n hasListProperty: false,\n });\n } else if (tokens[0] === 'property') {\n const element = building[building.length - 1];\n if (!element) throw new Error(`PLY property declared before any element: \"${line.trim()}\"`);\n if (tokens[1] === 'list') {\n // A list makes this element's records variable-size. That is fine for\n // the element itself (a trailing `face` list is common) - it only\n // costs later elements their computable offset. The types are recorded\n // so a reader that does want the data can walk it record by record.\n const countType = tokens[2] ?? '';\n const itemType = tokens[3] ?? '';\n const listName = tokens[4];\n if (\n PROPERTY_SIZES[countType] === undefined ||\n PROPERTY_SIZES[itemType] === undefined ||\n listName === undefined\n ) {\n throw new Error(`Unsupported PLY property: \"${line.trim()}\"`);\n }\n if (element.properties.has(listName) || element.listProperties.has(listName)) {\n throw new Error(`Duplicate PLY property \"${listName}\" in element \"${element.name}\".`);\n }\n element.listProperties.set(listName, { countType, itemType });\n element.hasListProperty = true;\n continue;\n }\n const type = tokens[1] ?? '';\n const size = PROPERTY_SIZES[type];\n const name = tokens[2];\n if (size === undefined || name === undefined) {\n throw new Error(`Unsupported PLY property: \"${line.trim()}\"`);\n }\n if (element.properties.has(name) || element.listProperties.has(name)) {\n // A duplicate name would let the later offset overwrite the earlier\n // one while the stride counts both - every read after it lands on\n // the wrong bytes.\n throw new Error(`Duplicate PLY property \"${name}\" in element \"${element.name}\".`);\n }\n element.properties.set(name, { type, offset: element.stride });\n element.propertyNames.push(name);\n element.stride += size;\n }\n }\n\n // Records are laid out back to back in element order, starting right after\n // the header - so each element begins where the previous one ended, until a\n // variable-size element makes the rest unreachable by arithmetic.\n let offset = endMatch.index + endMatch[0].length;\n let resolved = true;\n const elements: PlyElement[] = [];\n for (const element of building) {\n elements.push({\n name: element.name,\n count: element.count,\n properties: element.properties,\n propertyNames: element.propertyNames,\n listProperties: element.listProperties,\n stride: element.stride,\n offset: resolved ? offset : -1,\n hasListProperty: element.hasListProperty,\n });\n if (element.hasListProperty) resolved = false;\n else offset += element.count * element.stride;\n }\n\n return {\n elements,\n element: (name: string) => elements.find((candidate) => candidate.name === name),\n };\n}\n\n/**\n * Asserts an element's records are actually present in a file of `totalBytes`.\n *\n * Takes a size rather than a buffer so it works for a streamed parse, which\n * only ever holds a window of the file.\n *\n * @throws {Error} if the element sits behind a variable-size one, or its\n * records run past the end of the file.\n */\nexport function assertPlyElementFits(element: PlyElement, totalBytes: number): void {\n if (element.offset < 0) {\n throw new Error(\n `Unsupported PLY layout: element \"${element.name}\" follows a list property, so its ` +\n 'position cannot be determined.',\n );\n }\n const end = element.offset + element.count * element.stride;\n if (end > totalBytes) {\n throw new Error(\n `Truncated PLY file: element \"${element.name}\" needs ${end} bytes but the file is ` +\n `${totalBytes}.`,\n );\n }\n}\n\n/**\n * Byte size of a PLY scalar type.\n *\n * @throws {Error} if the type is not one PLY can declare.\n */\nexport function plyTypeSize(type: string): number {\n const size = PROPERTY_SIZES[type];\n if (size === undefined) throw new Error(`Unsupported PLY property type: \"${type}\".`);\n return size;\n}\n\n/** The byte offset of `property` within `element`'s record. */\nexport function plyPropertyOffset(element: PlyElement, property: string): number {\n const found = element.properties.get(property);\n if (!found) {\n throw new Error(`PLY element \"${element.name}\" is missing property \"${property}\".`);\n }\n return found.offset;\n}\n\n/** Asserts every named property exists on `element` with the given type. */\nexport function assertPlyPropertyTypes(\n element: PlyElement,\n properties: readonly string[],\n types: readonly string[],\n what: string,\n): void {\n for (const name of properties) {\n const property = element.properties.get(name);\n if (!property) {\n throw new Error(`${what}: missing property \"${name}\".`);\n }\n if (!types.includes(property.type)) {\n throw new Error(\n `Unsupported PLY property type for \"${name}\": expected ${types.join(' or ')}, ` +\n `got ${property.type}.`,\n );\n }\n }\n}\n"],"names":["PROPERTY_SIZES","parsePlyHeader","buffer","headerBytes","headerText","endMatch","building","line","tokens","name","count","element","countType","itemType","listName","type","size","offset","resolved","elements","candidate","assertPlyElementFits","totalBytes","end","plyTypeSize","plyPropertyOffset","property","found","assertPlyPropertyTypes","properties","types","what"],"mappings":"AAWA,MAAMA,IAAyC;AAAA,EAC7C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AACT;AA+DO,SAASC,EAAeC,GAAgC;AAC7D,QAAMC,IAAc,IAAI,WAAWD,GAAQ,GAAG,KAAK,IAAIA,EAAO,YAAY,KAAS,CAAC,GAC9EE,IAAa,IAAI,YAAY,OAAO,EAAE,OAAOD,CAAW,GACxDE,IAAW,kBAAkB,KAAKD,CAAU;AAClD,MAAI,CAAC,YAAY,KAAKA,CAAU;AAC9B,UAAM,IAAI,MAAM,uCAAuC;AAEzD,MAAI,CAACC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAIJ,MAAI,CAACD,EAAW,SAAS,iCAAiC;AACxD,UAAM,IAAI,MAAM,4DAA4D;AAY9E,QAAME,IAAuB,CAAA;AAE7B,aAAWC,KAAQH,EAAW,MAAM,GAAGC,EAAS,KAAK,EAAE,MAAM;AAAA,CAAI,GAAG;AAClE,UAAMG,IAASD,EAAK,KAAA,EAAO,MAAM,KAAK;AACtC,QAAIC,EAAO,CAAC,MAAM,WAAW;AAC3B,YAAMC,IAAOD,EAAO,CAAC,GACfE,IAAQ,OAAOF,EAAO,CAAC,CAAC;AAC9B,UAAIC,MAAS,OAAW,OAAM,IAAI,MAAM,2BAA2BF,EAAK,MAAM,GAAG;AACjF,UAAI,CAAC,OAAO,cAAcG,CAAK,KAAKA,IAAQ;AAC1C,cAAM,IAAI,MAAM,eAAeD,CAAI,WAAWD,EAAO,CAAC,CAAC,GAAG;AAE5D,MAAAF,EAAS,KAAK;AAAA,QACZ,MAAAG;AAAA,QACA,OAAAC;AAAA,QACA,gCAAgB,IAAA;AAAA,QAChB,eAAe,CAAA;AAAA,QACf,oCAAoB,IAAA;AAAA,QACpB,QAAQ;AAAA,QACR,iBAAiB;AAAA,MAAA,CAClB;AAAA,IACH,WAAWF,EAAO,CAAC,MAAM,YAAY;AACnC,YAAMG,IAAUL,EAASA,EAAS,SAAS,CAAC;AAC5C,UAAI,CAACK,EAAS,OAAM,IAAI,MAAM,8CAA8CJ,EAAK,MAAM,GAAG;AAC1F,UAAIC,EAAO,CAAC,MAAM,QAAQ;AAKxB,cAAMI,IAAYJ,EAAO,CAAC,KAAK,IACzBK,IAAWL,EAAO,CAAC,KAAK,IACxBM,IAAWN,EAAO,CAAC;AACzB,YACER,EAAeY,CAAS,MAAM,UAC9BZ,EAAea,CAAQ,MAAM,UAC7BC,MAAa;AAEb,gBAAM,IAAI,MAAM,8BAA8BP,EAAK,KAAA,CAAM,GAAG;AAE9D,YAAII,EAAQ,WAAW,IAAIG,CAAQ,KAAKH,EAAQ,eAAe,IAAIG,CAAQ;AACzE,gBAAM,IAAI,MAAM,2BAA2BA,CAAQ,iBAAiBH,EAAQ,IAAI,IAAI;AAEtF,QAAAA,EAAQ,eAAe,IAAIG,GAAU,EAAE,WAAAF,GAAW,UAAAC,GAAU,GAC5DF,EAAQ,kBAAkB;AAC1B;AAAA,MACF;AACA,YAAMI,IAAOP,EAAO,CAAC,KAAK,IACpBQ,IAAOhB,EAAee,CAAI,GAC1BN,IAAOD,EAAO,CAAC;AACrB,UAAIQ,MAAS,UAAaP,MAAS;AACjC,cAAM,IAAI,MAAM,8BAA8BF,EAAK,KAAA,CAAM,GAAG;AAE9D,UAAII,EAAQ,WAAW,IAAIF,CAAI,KAAKE,EAAQ,eAAe,IAAIF,CAAI;AAIjE,cAAM,IAAI,MAAM,2BAA2BA,CAAI,iBAAiBE,EAAQ,IAAI,IAAI;AAElF,MAAAA,EAAQ,WAAW,IAAIF,GAAM,EAAE,MAAAM,GAAM,QAAQJ,EAAQ,QAAQ,GAC7DA,EAAQ,cAAc,KAAKF,CAAI,GAC/BE,EAAQ,UAAUK;AAAA,IACpB;AAAA,EACF;AAKA,MAAIC,IAASZ,EAAS,QAAQA,EAAS,CAAC,EAAE,QACtCa,IAAW;AACf,QAAMC,IAAyB,CAAA;AAC/B,aAAWR,KAAWL;AACpB,IAAAa,EAAS,KAAK;AAAA,MACZ,MAAMR,EAAQ;AAAA,MACd,OAAOA,EAAQ;AAAA,MACf,YAAYA,EAAQ;AAAA,MACpB,eAAeA,EAAQ;AAAA,MACvB,gBAAgBA,EAAQ;AAAA,MACxB,QAAQA,EAAQ;AAAA,MAChB,QAAQO,IAAWD,IAAS;AAAA,MAC5B,iBAAiBN,EAAQ;AAAA,IAAA,CAC1B,GACGA,EAAQ,kBAAiBO,IAAW,KACnCD,KAAUN,EAAQ,QAAQA,EAAQ;AAGzC,SAAO;AAAA,IACL,UAAAQ;AAAA,IACA,SAAS,CAACV,MAAiBU,EAAS,KAAK,CAACC,MAAcA,EAAU,SAASX,CAAI;AAAA,EAAA;AAEnF;AAWO,SAASY,EAAqBV,GAAqBW,GAA0B;AAClF,MAAIX,EAAQ,SAAS;AACnB,UAAM,IAAI;AAAA,MACR,oCAAoCA,EAAQ,IAAI;AAAA,IAAA;AAIpD,QAAMY,IAAMZ,EAAQ,SAASA,EAAQ,QAAQA,EAAQ;AACrD,MAAIY,IAAMD;AACR,UAAM,IAAI;AAAA,MACR,gCAAgCX,EAAQ,IAAI,WAAWY,CAAG,0BACrDD,CAAU;AAAA,IAAA;AAGrB;AAOO,SAASE,EAAYT,GAAsB;AAChD,QAAMC,IAAOhB,EAAee,CAAI;AAChC,MAAIC,MAAS,OAAW,OAAM,IAAI,MAAM,mCAAmCD,CAAI,IAAI;AACnF,SAAOC;AACT;AAGO,SAASS,EAAkBd,GAAqBe,GAA0B;AAC/E,QAAMC,IAAQhB,EAAQ,WAAW,IAAIe,CAAQ;AAC7C,MAAI,CAACC;AACH,UAAM,IAAI,MAAM,gBAAgBhB,EAAQ,IAAI,0BAA0Be,CAAQ,IAAI;AAEpF,SAAOC,EAAM;AACf;AAGO,SAASC,EACdjB,GACAkB,GACAC,GACAC,GACM;AACN,aAAWtB,KAAQoB,GAAY;AAC7B,UAAMH,IAAWf,EAAQ,WAAW,IAAIF,CAAI;AAC5C,QAAI,CAACiB;AACH,YAAM,IAAI,MAAM,GAAGK,CAAI,uBAAuBtB,CAAI,IAAI;AAExD,QAAI,CAACqB,EAAM,SAASJ,EAAS,IAAI;AAC/B,YAAM,IAAI;AAAA,QACR,sCAAsCjB,CAAI,eAAeqB,EAAM,KAAK,MAAM,CAAC,SAClEJ,EAAS,IAAI;AAAA,MAAA;AAAA,EAG5B;AACF;"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
var Dt = Object.defineProperty;
|
|
2
|
+
var Ot = (u, n, e) => n in u ? Dt(u, n, { enumerable: !0, configurable: !0, writable: !0, value: e }) : u[n] = e;
|
|
3
|
+
var r = (u, n, e) => Ot(u, typeof n != "symbol" ? n + "" : n, e);
|
|
4
|
+
import * as h from "three/webgpu";
|
|
5
|
+
import { uniform as z, storage as m, Fn as A, If as l, float as C, instanceIndex as i, ivec2 as Mt, int as G, textureLoad as Kt, floatBitsToUint as Vt, uint as t, atomicStore as D, workgroupId as tt, Loop as B, invocationLocalIndex as bt, atomicAdd as Lt, atomicLoad as I, workgroupArray as Pt, storageBarrier as H, workgroupBarrier as q, atomicOr as Wt, countOneBits as et } from "three/tsl";
|
|
6
|
+
import { S as Xt, v as Nt, r as zt } from "./relighting-CSQF3UcL.js";
|
|
7
|
+
const Rt = 24, Gt = 32, nt = 4, xt = (1 << Rt) - 1;
|
|
8
|
+
function Ht(u) {
|
|
9
|
+
if (!Number.isInteger(u) || u <= 0 || u % nt !== 0)
|
|
10
|
+
throw new RangeError("Radix key width must be a positive multiple of four bits.");
|
|
11
|
+
return u / nt;
|
|
12
|
+
}
|
|
13
|
+
const c = 256, it = 8, st = c * it, O = 16, _ = c / 32, Y = O * _, Z = 256;
|
|
14
|
+
class jt {
|
|
15
|
+
constructor(n) {
|
|
16
|
+
r(this, "kind", "radix");
|
|
17
|
+
r(this, "renderer");
|
|
18
|
+
r(this, "stages", []);
|
|
19
|
+
r(this, "workingAttributes");
|
|
20
|
+
/** Frees the JS mirrors three keeps behind the GPU-only ping-pong buffers. */
|
|
21
|
+
r(this, "mirrors");
|
|
22
|
+
/** Set by {@link dispose}; makes a second dispose a no-op. */
|
|
23
|
+
r(this, "disposed", !1);
|
|
24
|
+
r(this, "viewRow2", z(new h.Vector4()));
|
|
25
|
+
r(this, "depthMin", z(0));
|
|
26
|
+
r(this, "depthScale", z(0));
|
|
27
|
+
r(this, "activeCount", z(0));
|
|
28
|
+
r(this, "viewCenter", new h.Vector3());
|
|
29
|
+
/** Exact mode avoids scene-bounds quantization entirely. */
|
|
30
|
+
r(this, "exactDepth");
|
|
31
|
+
const { capacity: e, centersTexture: f, dataTextureWidth: g, centersBuffer: v } = n;
|
|
32
|
+
if (!f && !v)
|
|
33
|
+
throw new Error("RadixSorter: provide centersTexture or centersBuffer.");
|
|
34
|
+
if (f && g === void 0)
|
|
35
|
+
throw new Error("RadixSorter: dataTextureWidth is required with centersTexture.");
|
|
36
|
+
this.renderer = n.renderer;
|
|
37
|
+
const S = n.exactDepth === !0;
|
|
38
|
+
this.exactDepth = S;
|
|
39
|
+
const F = Ht(S ? Gt : Rt), p = Math.ceil(e / st), k = O * p, M = Math.ceil(k / Z), rt = new h.StorageBufferAttribute(new Uint32Array(e), 1), ot = new h.StorageBufferAttribute(new Uint32Array(e), 1), at = new h.StorageBufferAttribute(new Uint32Array(e), 1), ct = new h.StorageBufferAttribute(new Uint32Array(e), 1), ut = new h.StorageBufferAttribute(
|
|
40
|
+
new Uint32Array(k),
|
|
41
|
+
1
|
|
42
|
+
), dt = new h.StorageBufferAttribute(new Uint32Array(M), 1), lt = new h.StorageBufferAttribute(
|
|
43
|
+
new Uint32Array(p * Y),
|
|
44
|
+
1
|
|
45
|
+
);
|
|
46
|
+
this.workingAttributes = [
|
|
47
|
+
rt,
|
|
48
|
+
ot,
|
|
49
|
+
at,
|
|
50
|
+
ct,
|
|
51
|
+
ut,
|
|
52
|
+
dt,
|
|
53
|
+
lt
|
|
54
|
+
], this.mirrors = new Xt(this.workingAttributes);
|
|
55
|
+
const K = m(rt, "uint", e), mt = m(ot, "uint", e), V = m(at, "uint", e), ht = m(ct, "uint", e), vt = m(n.sourceIndexAttribute, "uint", e), St = m(n.splatIndexAttribute, "float", e), gt = v ? m(v, "vec4", e) : null, w = m(ut, "uint", k).toAtomic(), L = m(dt, "uint", M), T = m(lt, "uint", p * Y).toAtomic(), kt = A(() => {
|
|
56
|
+
l(C(i).lessThan(this.activeCount), () => {
|
|
57
|
+
const o = vt.element(i), P = f ? Mt(
|
|
58
|
+
G(o).mod(G(g)),
|
|
59
|
+
G(o).div(G(g))
|
|
60
|
+
) : null, j = gt ? gt.element(o).xyz : Kt(f, P).xyz, W = this.viewRow2.xyz.dot(j).add(this.viewRow2.w);
|
|
61
|
+
if (S) {
|
|
62
|
+
const X = Vt(W), N = X.shiftRight(t(31)).equal(t(0)).select(t(2147483648), t(4294967295));
|
|
63
|
+
K.element(i).assign(X.bitXor(N));
|
|
64
|
+
} else
|
|
65
|
+
K.element(i).assign(W.sub(this.depthMin).mul(this.depthScale).clamp(0, xt).toUint());
|
|
66
|
+
V.element(i).assign(o);
|
|
67
|
+
});
|
|
68
|
+
})().compute(e, [c]);
|
|
69
|
+
this.stages.push(kt);
|
|
70
|
+
for (let o = 0; o < F; o++) {
|
|
71
|
+
const P = o % 2 === 0 ? K : mt, j = o % 2 === 0 ? mt : K, W = o % 2 === 0 ? V : ht, X = o % 2 === 0 ? ht : V, N = t(o * nt), Bt = A(() => {
|
|
72
|
+
D(w.element(i), t(0));
|
|
73
|
+
})().compute(k, [c]), It = A(() => {
|
|
74
|
+
const s = tt.x.mul(t(st));
|
|
75
|
+
B(it, ({ i: a }) => {
|
|
76
|
+
const d = s.add(t(a).mul(t(c))).add(bt);
|
|
77
|
+
l(C(d).lessThan(this.activeCount), () => {
|
|
78
|
+
const b = P.element(d).shiftRight(N).bitAnd(t(15));
|
|
79
|
+
Lt(w.element(b.mul(t(p)).add(tt.x)), t(1));
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
})().compute(p * c, [c]), _t = A(() => {
|
|
83
|
+
const s = i.mul(t(Z)), a = t(0).toVar();
|
|
84
|
+
B(Z, ({ i: d }) => {
|
|
85
|
+
const b = s.add(t(d));
|
|
86
|
+
l(b.lessThan(t(k)), () => {
|
|
87
|
+
const E = I(w.element(b)).toVar();
|
|
88
|
+
D(w.element(b), a), a.addAssign(E);
|
|
89
|
+
});
|
|
90
|
+
}), L.element(i).assign(a);
|
|
91
|
+
})().compute(M, [64]), Et = A(() => {
|
|
92
|
+
l(i.equal(t(0)), () => {
|
|
93
|
+
const s = t(0).toVar();
|
|
94
|
+
B(M, ({ i: a }) => {
|
|
95
|
+
const d = L.element(a).toVar();
|
|
96
|
+
L.element(a).assign(s), s.addAssign(d);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
})().compute(1, [64]), yt = A(() => {
|
|
100
|
+
const s = i.div(t(Z));
|
|
101
|
+
D(
|
|
102
|
+
w.element(i),
|
|
103
|
+
I(w.element(i)).add(L.element(s))
|
|
104
|
+
);
|
|
105
|
+
})().compute(k, [c]), J = Pt("uint", O), Ut = A(() => {
|
|
106
|
+
const s = bt, a = tt.x, d = a.mul(t(Y));
|
|
107
|
+
l(s.lessThan(t(O)), () => {
|
|
108
|
+
J.element(s).assign(t(0));
|
|
109
|
+
}), l(s.lessThan(t(Y)), () => {
|
|
110
|
+
D(T.element(d.add(s)), t(0));
|
|
111
|
+
}), H(), q(), B(it, ({ i: b }) => {
|
|
112
|
+
const E = a.mul(t(st)).add(b.toUint().mul(t(c))).add(s), At = C(E).lessThan(this.activeCount), Q = t(0).toVar(), ft = t(0).toVar(), y = t(0).toVar();
|
|
113
|
+
l(At, () => {
|
|
114
|
+
Q.assign(P.element(E)), ft.assign(W.element(E)), y.assign(Q.shiftRight(N).bitAnd(t(15)));
|
|
115
|
+
const x = s.shiftRight(t(5)), U = s.bitAnd(t(31)), R = d.add(y.mul(t(_))).add(x);
|
|
116
|
+
Wt(T.element(R), t(1).shiftLeft(U));
|
|
117
|
+
}), H(), q(), l(At, () => {
|
|
118
|
+
const x = s.shiftRight(t(5)), U = s.bitAnd(t(31)), R = d.add(y.mul(t(_))), $ = J.element(y).toVar();
|
|
119
|
+
B(_, ({ i: wt }) => {
|
|
120
|
+
l(t(wt).lessThan(x), () => {
|
|
121
|
+
$.addAssign(
|
|
122
|
+
et(I(T.element(R.add(wt.toUint()))))
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
const Ct = t(1).shiftLeft(U).sub(t(1));
|
|
127
|
+
$.addAssign(
|
|
128
|
+
et(I(T.element(R.add(x))).bitAnd(Ct))
|
|
129
|
+
);
|
|
130
|
+
const pt = I(w.element(y.mul(t(p)).add(a))).add($);
|
|
131
|
+
j.element(pt).assign(Q), X.element(pt).assign(ft);
|
|
132
|
+
}), H(), q(), l(s.lessThan(t(O)), () => {
|
|
133
|
+
const x = t(0).toVar();
|
|
134
|
+
B(_, ({ i: U }) => {
|
|
135
|
+
const R = d.add(s.mul(t(_))).add(U.toUint());
|
|
136
|
+
x.addAssign(et(I(T.element(R)))), D(T.element(R), t(0));
|
|
137
|
+
}), J.element(s).addAssign(x);
|
|
138
|
+
}), H(), q();
|
|
139
|
+
});
|
|
140
|
+
})().compute(p * c, [c]);
|
|
141
|
+
this.stages.push(
|
|
142
|
+
Bt,
|
|
143
|
+
It,
|
|
144
|
+
_t,
|
|
145
|
+
Et,
|
|
146
|
+
yt,
|
|
147
|
+
Ut
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const Tt = A(() => {
|
|
151
|
+
l(C(i).lessThan(this.activeCount), () => {
|
|
152
|
+
St.element(i).assign(C(V.element(i)));
|
|
153
|
+
});
|
|
154
|
+
})().compute(e, [c]);
|
|
155
|
+
this.stages.push(Tt);
|
|
156
|
+
}
|
|
157
|
+
sort(n, e, f) {
|
|
158
|
+
if (e === 0) return !0;
|
|
159
|
+
const g = n.elements;
|
|
160
|
+
if (this.viewRow2.value.set(g[2], g[6], g[10], g[14]), this.activeCount.value = e, !this.exactDepth) {
|
|
161
|
+
this.viewCenter.copy(f.center).applyMatrix4(n);
|
|
162
|
+
const v = Nt(n, f.radius), S = this.viewCenter.z - v, F = this.viewCenter.z + v;
|
|
163
|
+
this.depthMin.value = S, this.depthScale.value = xt / (F - S || 1);
|
|
164
|
+
}
|
|
165
|
+
return this.stages[0].count = e, this.stages[this.stages.length - 1].count = e, this.renderer.compute(this.stages), this.mirrors.settled || this.mirrors.release(this.renderer), !0;
|
|
166
|
+
}
|
|
167
|
+
dispose() {
|
|
168
|
+
if (!this.disposed) {
|
|
169
|
+
this.disposed = !0;
|
|
170
|
+
for (const n of this.stages) n.dispose();
|
|
171
|
+
zt(this.renderer, this.workingAttributes);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
jt as RadixSorter
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=radix-sorter-R0FCSqKu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radix-sorter-R0FCSqKu.js","sources":["../src/lib/radix-sort.ts","../src/lib/radix-sorter.ts"],"sourcesContent":["/**\n * @role Utility\n * Shared constants and deterministic reference logic for GPU radix sorting.\n */\n\n/** Depth precision retained by both the GPU radix path and WebGL worker path. */\nexport const RADIX_KEY_BITS = 24;\n/** Exact Float32 depth keys use every sortable IEEE-754 bit. */\nexport const RADIX_EXACT_KEY_BITS = 32;\n/** Four-bit digits keep the portable shader's workgroup memory requirements small. */\nexport const RADIX_BITS_PER_PASS = 4;\nexport const RADIX_BUCKETS = 1 << RADIX_BITS_PER_PASS;\nexport const RADIX_PASS_COUNT = RADIX_KEY_BITS / RADIX_BITS_PER_PASS;\nexport const RADIX_KEY_MAX = (1 << RADIX_KEY_BITS) - 1;\n\n/** Number of four-bit passes required for one unsigned radix key width. */\nexport function radixPassCount(keyBits: number): number {\n if (!Number.isInteger(keyBits) || keyBits <= 0 || keyBits % RADIX_BITS_PER_PASS !== 0) {\n throw new RangeError('Radix key width must be a positive multiple of four bits.');\n }\n return keyBits / RADIX_BITS_PER_PASS;\n}\n\n/**\n * Maps a Float32 bit pattern onto unsigned integer order without losing depth\n * precision. Ascending mapped keys are ascending numeric floats, including\n * negative view-space Z values (farther splats first in VLAM's convention).\n */\nexport function sortableFloat32Bits(bits: number): number {\n const value = bits >>> 0;\n return (value ^ ((value & 0x80000000) === 0 ? 0x80000000 : 0xffffffff)) >>> 0;\n}\n\n/** Quantizes a depth into the unsigned 24-bit key used by the GPU sorter. */\nexport function quantizeDepthKey(depth: number, minimum: number, maximum: number): number {\n const span = maximum - minimum;\n if (!Number.isFinite(depth) || !Number.isFinite(span) || span <= 0) return 0;\n return Math.min(\n RADIX_KEY_MAX,\n Math.max(0, Math.round(((depth - minimum) / span) * RADIX_KEY_MAX)),\n );\n}\n\n/**\n * Stable CPU reference for validating the GPU's six four-bit radix passes.\n * This is deliberately test/debug-only logic, not the WebGL production sorter.\n */\nexport function stableRadixSortReference(\n keys: Uint32Array,\n values?: Uint32Array,\n keyBits = RADIX_KEY_BITS,\n): Uint32Array {\n const count = keys.length;\n let inputKeys = Uint32Array.from(keys);\n let outputKeys = new Uint32Array(count);\n let inputValues = values ? Uint32Array.from(values) : Uint32Array.from(keys.keys());\n let outputValues = new Uint32Array(count);\n const counts = new Uint32Array(RADIX_BUCKETS);\n const offsets = new Uint32Array(RADIX_BUCKETS);\n\n for (let pass = 0; pass < radixPassCount(keyBits); pass++) {\n counts.fill(0);\n const shift = pass * RADIX_BITS_PER_PASS;\n for (let i = 0; i < count; i++) {\n const digit = ((inputKeys[i] as number) >>> shift) & 0xf;\n counts[digit] = (counts[digit] as number) + 1;\n }\n let total = 0;\n for (let digit = 0; digit < RADIX_BUCKETS; digit++) {\n offsets[digit] = total;\n total += counts[digit] as number;\n }\n for (let i = 0; i < count; i++) {\n const key = inputKeys[i] as number;\n const digit = (key >>> shift) & 0xf;\n const target = offsets[digit] as number;\n offsets[digit] = target + 1;\n outputKeys[target] = key;\n outputValues[target] = inputValues[i] as number;\n }\n [inputKeys, outputKeys] = [outputKeys, inputKeys];\n [inputValues, outputValues] = [outputValues, inputValues];\n }\n return inputValues;\n}\n","/**\n * @role Bridge\n * Portable stable WebGPU radix sorting adapted to Three.js TSL compute nodes.\n */\nimport * as THREE from 'three/webgpu';\nimport {\n Fn,\n If,\n Loop,\n atomicAdd,\n atomicLoad,\n atomicOr,\n atomicStore,\n countOneBits,\n float,\n floatBitsToUint,\n instanceIndex,\n int,\n invocationLocalIndex,\n ivec2,\n storage,\n storageBarrier,\n textureLoad,\n uint,\n uniform,\n workgroupArray,\n workgroupBarrier,\n workgroupId,\n} from 'three/tsl';\nimport type { SplatSorter } from './sorter';\nimport { releaseRendererAttributes } from './compute-sorter';\nimport {\n RADIX_BITS_PER_PASS,\n RADIX_EXACT_KEY_BITS,\n RADIX_KEY_BITS,\n RADIX_KEY_MAX,\n radixPassCount,\n} from './radix-sort';\nimport { viewDepthRadius } from './splat-sort-bounds';\nimport { StorageMirrorReleaser } from './storage-attribute-mirror';\n\nconst WORKGROUP_SIZE = 256;\nconst ELEMENTS_PER_THREAD = 8;\nconst ELEMENTS_PER_WORKGROUP = WORKGROUP_SIZE * ELEMENTS_PER_THREAD;\nconst DIGIT_COUNT = 16;\nconst MASK_WORDS_PER_DIGIT = WORKGROUP_SIZE / 32;\nconst MASK_WORDS_PER_GROUP = DIGIT_COUNT * MASK_WORDS_PER_DIGIT;\nconst SCAN_BLOCK_SIZE = 256;\n\ntype UintWorkgroupArray = {\n element(index: THREE.Node<'uint'>): THREE.Node<'uint'>;\n};\n\n/** Isolates gaps in Three's current WorkgroupInfo/Bitcount TypeScript declarations. */\nfunction asUintNode(node: unknown): THREE.Node<'uint'> {\n return node as THREE.Node<'uint'>;\n}\n\n/**\n * Stable four-bit-per-pass WebGPU radix sorter.\n *\n * The ranked scatter follows PlayCanvas/WebGPU-Radix-Sort's per-digit bitmask\n * design. Three.js does not currently expose atomic workgroup arrays in TSL,\n * so masks use disjoint global-storage slices per workgroup. Workgroup and\n * storage barriers retain the same stable ranking semantics without relying\n * on cross-workgroup execution order.\n */\nexport class RadixSorter implements SplatSorter {\n readonly kind = 'radix' as const;\n private readonly renderer: THREE.WebGPURenderer;\n private readonly stages: THREE.ComputeNode[] = [];\n private readonly workingAttributes: THREE.StorageBufferAttribute[];\n /** Frees the JS mirrors three keeps behind the GPU-only ping-pong buffers. */\n private readonly mirrors: StorageMirrorReleaser;\n /** Set by {@link dispose}; makes a second dispose a no-op. */\n private disposed = false;\n private readonly viewRow2 = uniform(new THREE.Vector4());\n private readonly depthMin = uniform(0);\n private readonly depthScale = uniform(0);\n private readonly activeCount = uniform(0);\n private readonly viewCenter = new THREE.Vector3();\n /** Exact mode avoids scene-bounds quantization entirely. */\n private readonly exactDepth: boolean;\n\n constructor(options: {\n renderer: THREE.WebGPURenderer;\n capacity: number;\n centersTexture?: THREE.DataTexture;\n dataTextureWidth?: number;\n /** Gathered world-space centers for a unified renderer. */\n centersBuffer?: THREE.StorageBufferAttribute;\n splatIndexAttribute: THREE.StorageInstancedBufferAttribute;\n sourceIndexAttribute: THREE.StorageBufferAttribute;\n /** Keep every Float32 depth bit instead of quantizing to 24 bits. */\n exactDepth?: boolean;\n }) {\n const { capacity, centersTexture, dataTextureWidth, centersBuffer } = options;\n if (!centersTexture && !centersBuffer) {\n throw new Error('RadixSorter: provide centersTexture or centersBuffer.');\n }\n if (centersTexture && dataTextureWidth === undefined) {\n throw new Error('RadixSorter: dataTextureWidth is required with centersTexture.');\n }\n this.renderer = options.renderer;\n const exactDepth = options.exactDepth === true;\n this.exactDepth = exactDepth;\n const passCount = radixPassCount(exactDepth ? RADIX_EXACT_KEY_BITS : RADIX_KEY_BITS);\n const groupCount = Math.ceil(capacity / ELEMENTS_PER_WORKGROUP);\n const histogramLength = DIGIT_COUNT * groupCount;\n const scanBlockCount = Math.ceil(histogramLength / SCAN_BLOCK_SIZE);\n\n const keysAAttribute = new THREE.StorageBufferAttribute(new Uint32Array(capacity), 1);\n const keysBAttribute = new THREE.StorageBufferAttribute(new Uint32Array(capacity), 1);\n const valuesAAttribute = new THREE.StorageBufferAttribute(new Uint32Array(capacity), 1);\n const valuesBAttribute = new THREE.StorageBufferAttribute(new Uint32Array(capacity), 1);\n const histogramAttribute = new THREE.StorageBufferAttribute(\n new Uint32Array(histogramLength),\n 1,\n );\n const scanSumsAttribute = new THREE.StorageBufferAttribute(new Uint32Array(scanBlockCount), 1);\n const masksAttribute = new THREE.StorageBufferAttribute(\n new Uint32Array(groupCount * MASK_WORDS_PER_GROUP),\n 1,\n );\n this.workingAttributes = [\n keysAAttribute,\n keysBAttribute,\n valuesAAttribute,\n valuesBAttribute,\n histogramAttribute,\n scanSumsAttribute,\n masksAttribute,\n ];\n this.mirrors = new StorageMirrorReleaser(this.workingAttributes);\n\n const keysA = storage(keysAAttribute, 'uint', capacity);\n const keysB = storage(keysBAttribute, 'uint', capacity);\n const valuesA = storage(valuesAAttribute, 'uint', capacity);\n const valuesB = storage(valuesBAttribute, 'uint', capacity);\n const source = storage(options.sourceIndexAttribute, 'uint', capacity);\n const order = storage(options.splatIndexAttribute, 'float', capacity);\n const workCenters = centersBuffer ? storage(centersBuffer, 'vec4', capacity) : null;\n const histogram = storage(histogramAttribute, 'uint', histogramLength).toAtomic();\n const scanSums = storage(scanSumsAttribute, 'uint', scanBlockCount);\n const masks = storage(masksAttribute, 'uint', groupCount * MASK_WORDS_PER_GROUP).toAtomic();\n\n const buildKeys = Fn(() => {\n If(float(instanceIndex).lessThan(this.activeCount), () => {\n const poolIndex = source.element(instanceIndex);\n const texel = centersTexture\n ? ivec2(\n int(poolIndex).mod(int(dataTextureWidth as number)),\n int(poolIndex).div(int(dataTextureWidth as number)),\n )\n : null;\n const center = workCenters\n ? workCenters.element(poolIndex).xyz\n : textureLoad(centersTexture, texel as THREE.Node<'ivec2'>).xyz;\n const depth = this.viewRow2.xyz.dot(center).add(this.viewRow2.w);\n if (exactDepth) {\n // IEEE-754 bits are monotonic only for positive values. Flip the\n // sign partition so ascending unsigned keys remain ascending numeric\n // depth, including VLAM's negative view-space Z convention.\n // Three's BitcastNode typings omit shift/xor; runtime nodes support them.\n const bits = asUintNode(floatBitsToUint(depth));\n const mask = bits\n .shiftRight(uint(31))\n .equal(uint(0))\n .select(uint(0x80000000), uint(0xffffffff));\n keysA.element(instanceIndex).assign(bits.bitXor(mask));\n } else {\n keysA\n .element(instanceIndex)\n .assign(depth.sub(this.depthMin).mul(this.depthScale).clamp(0, RADIX_KEY_MAX).toUint());\n }\n valuesA.element(instanceIndex).assign(poolIndex);\n });\n })().compute(capacity, [WORKGROUP_SIZE]);\n this.stages.push(buildKeys);\n\n for (let pass = 0; pass < passCount; pass++) {\n const inputKeys = pass % 2 === 0 ? keysA : keysB;\n const outputKeys = pass % 2 === 0 ? keysB : keysA;\n const inputValues = pass % 2 === 0 ? valuesA : valuesB;\n const outputValues = pass % 2 === 0 ? valuesB : valuesA;\n const bit = uint(pass * RADIX_BITS_PER_PASS);\n\n const clearHistogram = Fn(() => {\n atomicStore(histogram.element(instanceIndex), uint(0));\n })().compute(histogramLength, [WORKGROUP_SIZE]);\n\n const buildHistogram = Fn(() => {\n const base = workgroupId.x.mul(uint(ELEMENTS_PER_WORKGROUP));\n Loop(ELEMENTS_PER_THREAD, ({ i }) => {\n const index = base.add(uint(i).mul(uint(WORKGROUP_SIZE))).add(invocationLocalIndex);\n If(float(index).lessThan(this.activeCount), () => {\n const digit = inputKeys.element(index).shiftRight(bit).bitAnd(uint(0xf));\n atomicAdd(histogram.element(digit.mul(uint(groupCount)).add(workgroupId.x)), uint(1));\n });\n });\n })().compute(groupCount * WORKGROUP_SIZE, [WORKGROUP_SIZE]);\n\n const scanBlocks = Fn(() => {\n const start = instanceIndex.mul(uint(SCAN_BLOCK_SIZE));\n const running = uint(0).toVar();\n Loop(SCAN_BLOCK_SIZE, ({ i }) => {\n const index = start.add(uint(i));\n If(index.lessThan(uint(histogramLength)), () => {\n const value = atomicLoad(histogram.element(index)).toVar();\n atomicStore(histogram.element(index), running);\n running.addAssign(value);\n });\n });\n scanSums.element(instanceIndex).assign(running);\n })().compute(scanBlockCount, [64]);\n\n const scanBlockSums = Fn(() => {\n If(instanceIndex.equal(uint(0)), () => {\n const running = uint(0).toVar();\n Loop(scanBlockCount, ({ i }) => {\n const value = scanSums.element(i).toVar();\n scanSums.element(i).assign(running);\n running.addAssign(value);\n });\n });\n })().compute(1, [64]);\n\n const addBlockOffsets = Fn(() => {\n const block = instanceIndex.div(uint(SCAN_BLOCK_SIZE));\n atomicStore(\n histogram.element(instanceIndex),\n atomicLoad(histogram.element(instanceIndex)).add(scanSums.element(block)),\n );\n })().compute(histogramLength, [WORKGROUP_SIZE]);\n\n const digitOffsets = workgroupArray('uint', DIGIT_COUNT) as unknown as UintWorkgroupArray;\n const rankedScatter = Fn(() => {\n const thread = invocationLocalIndex;\n const group = workgroupId.x;\n const maskBase = group.mul(uint(MASK_WORDS_PER_GROUP));\n If(thread.lessThan(uint(DIGIT_COUNT)), () => {\n digitOffsets.element(thread).assign(uint(0));\n });\n If(thread.lessThan(uint(MASK_WORDS_PER_GROUP)), () => {\n atomicStore(masks.element(maskBase.add(thread)), uint(0));\n });\n storageBarrier();\n workgroupBarrier();\n\n Loop(ELEMENTS_PER_THREAD, ({ i }) => {\n const index = group\n .mul(uint(ELEMENTS_PER_WORKGROUP))\n .add(i.toUint().mul(uint(WORKGROUP_SIZE)))\n .add(thread);\n const valid = float(index).lessThan(this.activeCount);\n const key = uint(0).toVar();\n const value = uint(0).toVar();\n const digit = uint(0).toVar();\n If(valid, () => {\n key.assign(inputKeys.element(index));\n value.assign(inputValues.element(index));\n digit.assign(key.shiftRight(bit).bitAnd(uint(0xf)));\n const word = thread.shiftRight(uint(5));\n const bitInWord = thread.bitAnd(uint(31));\n const maskIndex = maskBase.add(digit.mul(uint(MASK_WORDS_PER_DIGIT))).add(word);\n atomicOr(masks.element(maskIndex), uint(1).shiftLeft(bitInWord));\n });\n storageBarrier();\n workgroupBarrier();\n\n If(valid, () => {\n const word = thread.shiftRight(uint(5));\n const bitInWord = thread.bitAnd(uint(31));\n const digitBase = maskBase.add(digit.mul(uint(MASK_WORDS_PER_DIGIT)));\n const localRank = digitOffsets.element(digit).toVar();\n Loop(MASK_WORDS_PER_DIGIT, ({ i: wordIndex }) => {\n If(uint(wordIndex).lessThan(word), () => {\n localRank.addAssign(\n asUintNode(\n countOneBits(atomicLoad(masks.element(digitBase.add(wordIndex.toUint())))),\n ),\n );\n });\n });\n const precedingMask = uint(1).shiftLeft(bitInWord).sub(uint(1));\n localRank.addAssign(\n asUintNode(\n countOneBits(atomicLoad(masks.element(digitBase.add(word))).bitAnd(precedingMask)),\n ),\n );\n const prefix = atomicLoad(histogram.element(digit.mul(uint(groupCount)).add(group)));\n const target = prefix.add(localRank);\n outputKeys.element(target).assign(key);\n outputValues.element(target).assign(value);\n });\n\n storageBarrier();\n workgroupBarrier();\n If(thread.lessThan(uint(DIGIT_COUNT)), () => {\n const total = uint(0).toVar();\n Loop(MASK_WORDS_PER_DIGIT, ({ i: wordIndex }) => {\n const maskIndex = maskBase\n .add(thread.mul(uint(MASK_WORDS_PER_DIGIT)))\n .add(wordIndex.toUint());\n total.addAssign(asUintNode(countOneBits(atomicLoad(masks.element(maskIndex)))));\n atomicStore(masks.element(maskIndex), uint(0));\n });\n digitOffsets.element(thread).addAssign(total);\n });\n storageBarrier();\n workgroupBarrier();\n });\n })().compute(groupCount * WORKGROUP_SIZE, [WORKGROUP_SIZE]);\n\n this.stages.push(\n clearHistogram,\n buildHistogram,\n scanBlocks,\n scanBlockSums,\n addBlockOffsets,\n rankedScatter,\n );\n }\n\n const writeOrder = Fn(() => {\n If(float(instanceIndex).lessThan(this.activeCount), () => {\n order.element(instanceIndex).assign(float(valuesA.element(instanceIndex)));\n });\n })().compute(capacity, [WORKGROUP_SIZE]);\n this.stages.push(writeOrder);\n }\n\n sort(modelView: THREE.Matrix4, activeCount: number, bounds: THREE.Sphere): boolean {\n if (activeCount === 0) return true;\n const m = modelView.elements;\n this.viewRow2.value.set(m[2], m[6], m[10], m[14]);\n this.activeCount.value = activeCount;\n if (!this.exactDepth) {\n this.viewCenter.copy(bounds.center).applyMatrix4(modelView);\n const viewRadius = viewDepthRadius(modelView, bounds.radius);\n const minimum = this.viewCenter.z - viewRadius;\n const maximum = this.viewCenter.z + viewRadius;\n this.depthMin.value = minimum;\n this.depthScale.value = RADIX_KEY_MAX / (maximum - minimum || 1);\n }\n this.stages[0]!.count = activeCount;\n this.stages[this.stages.length - 1]!.count = activeCount;\n this.renderer.compute(this.stages);\n // Key/value ping-pong, histogram, scan sums and masks are all written and\n // read entirely on the GPU - at 4 B per splat of capacity each, their JS\n // mirrors are ~16 B/splat of pure waste. Deliberately *not*\n // `sourceIndex`/`splatIndex`: those belong to the mesh and are rewritten\n // from the CPU every frame. Same ownership line as `releaseRendererAttributes`.\n if (!this.mirrors.settled) this.mirrors.release(this.renderer);\n return true;\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n for (const stage of this.stages) stage.dispose();\n releaseRendererAttributes(this.renderer, this.workingAttributes);\n }\n}\n"],"names":["RADIX_KEY_BITS","RADIX_EXACT_KEY_BITS","RADIX_BITS_PER_PASS","RADIX_KEY_MAX","radixPassCount","keyBits","WORKGROUP_SIZE","ELEMENTS_PER_THREAD","ELEMENTS_PER_WORKGROUP","DIGIT_COUNT","MASK_WORDS_PER_DIGIT","MASK_WORDS_PER_GROUP","SCAN_BLOCK_SIZE","RadixSorter","options","__publicField","uniform","THREE","capacity","centersTexture","dataTextureWidth","centersBuffer","exactDepth","passCount","groupCount","histogramLength","scanBlockCount","keysAAttribute","keysBAttribute","valuesAAttribute","valuesBAttribute","histogramAttribute","scanSumsAttribute","masksAttribute","StorageMirrorReleaser","keysA","storage","keysB","valuesA","valuesB","source","order","workCenters","histogram","scanSums","masks","buildKeys","Fn","If","float","instanceIndex","poolIndex","texel","ivec2","int","center","textureLoad","depth","bits","floatBitsToUint","mask","uint","pass","inputKeys","outputKeys","inputValues","outputValues","bit","clearHistogram","atomicStore","buildHistogram","base","workgroupId","Loop","i","index","invocationLocalIndex","digit","atomicAdd","scanBlocks","start","running","value","atomicLoad","scanBlockSums","addBlockOffsets","block","digitOffsets","workgroupArray","rankedScatter","thread","group","maskBase","storageBarrier","workgroupBarrier","valid","key","word","bitInWord","maskIndex","atomicOr","digitBase","localRank","wordIndex","countOneBits","precedingMask","target","total","writeOrder","modelView","activeCount","bounds","m","viewRadius","viewDepthRadius","minimum","maximum","stage","releaseRendererAttributes"],"mappings":"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"}
|