gaussian-splat-lite 1.0.1 → 1.0.2
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/CHANGELOG.md +10 -1
- package/dist/gaussian-splat-lite.cjs +65 -75
- package/dist/gaussian-splat-lite.cjs.map +1 -1
- package/dist/gaussian-splat-lite.min.cjs +20 -20
- package/dist/gaussian-splat-lite.min.cjs.map +1 -1
- package/dist/gaussian-splat-lite.module.js +65 -75
- package/dist/gaussian-splat-lite.module.js.map +1 -1
- package/dist/gaussian-splat-lite.module.min.js +433 -441
- package/dist/gaussian-splat-lite.module.min.js.map +1 -1
- package/dist/types/rendering/webgpu/ProjectedSplats.d.cts +4 -2
- package/dist/types/rendering/webgpu/ProjectedSplats.d.ts +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.2] - 2026-09-11
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Allowed native WebGPU rendering after shared kernels, the full sorter, and the first projection slot compile, warming the remaining slots asynchronously.
|
|
15
|
+
- Used stable storage binding names to allow native WebGPU projection slots to share shader programs.
|
|
16
|
+
- Removed the separate native WebGPU indirect-draw reset dispatch by initializing fixed arguments once and updating instance counts in the projection finish pass, preserving the maximum count across views.
|
|
17
|
+
|
|
10
18
|
## [1.0.1] - 2026-09-11
|
|
11
19
|
|
|
12
20
|
### Added
|
|
@@ -270,7 +278,8 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
270
278
|
|
|
271
279
|
- Initial public release of the Three.js Gaussian Splatting renderer.
|
|
272
280
|
|
|
273
|
-
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.0.
|
|
281
|
+
[Unreleased]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.0.2...HEAD
|
|
282
|
+
[1.0.2]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.0.1...v1.0.2
|
|
274
283
|
[1.0.1]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v1.0.0...v1.0.1
|
|
275
284
|
[1.0.0]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v0.1.16...v1.0.0
|
|
276
285
|
[0.1.16]: https://github.com/WilliamLiu-1997/Gaussian-Splat-Lite/compare/v0.1.15...v0.1.16
|
|
@@ -26423,11 +26423,8 @@ const ELEMENTS_PER_THREAD = 8;
|
|
|
26423
26423
|
const ELEMENTS_PER_WORKGROUP = WORKGROUP_SIZE$1 * ELEMENTS_PER_THREAD;
|
|
26424
26424
|
const PREFIX_ITEMS_PER_WORKGROUP = WORKGROUP_SIZE$1 * 2;
|
|
26425
26425
|
const PREFIX_LEVELS = 3;
|
|
26426
|
-
function storage(buffer2,
|
|
26427
|
-
|
|
26428
|
-
const node = N$2.storage(getBuffer(), type).onObjectUpdate(getBuffer);
|
|
26429
|
-
node.name = name;
|
|
26430
|
-
return readOnly ? node.toReadOnly() : node;
|
|
26426
|
+
function storage(buffer2, name) {
|
|
26427
|
+
return N$2.storage(buffer2.value, "uint").setName(name).onObjectUpdate(() => buffer2.value);
|
|
26431
26428
|
}
|
|
26432
26429
|
function mutableUniform(value, type) {
|
|
26433
26430
|
const holder = { value };
|
|
@@ -26452,8 +26449,8 @@ function makeHistogramTask({
|
|
|
26452
26449
|
bitOffset,
|
|
26453
26450
|
workgroupCount
|
|
26454
26451
|
}) {
|
|
26455
|
-
const inputKeys = storage(input, "
|
|
26456
|
-
const sums = storage(blockSums, "
|
|
26452
|
+
const inputKeys = storage(input, "gslRadixInputKeys").toReadOnly();
|
|
26453
|
+
const sums = storage(blockSums, "gslRadixBlockSums");
|
|
26457
26454
|
const bit = N$2.uint(bitOffset);
|
|
26458
26455
|
const histogram = N$2.workgroupArray("uint", RADIX_BUCKETS).toAtomic().setName("gslRadixHistogram");
|
|
26459
26456
|
return N$2.Fn(() => {
|
|
@@ -26486,8 +26483,8 @@ function makeHistogramTask({
|
|
|
26486
26483
|
})().compute([1, 1, 1], [WORKGROUP_SIZE$1]).setName("Splat radix histogram");
|
|
26487
26484
|
}
|
|
26488
26485
|
function makePrefixScanTask(itemsBuffer, blockSumsBuffer, elementCount) {
|
|
26489
|
-
const items = storage(itemsBuffer, "
|
|
26490
|
-
const blockSums = storage(blockSumsBuffer, "
|
|
26486
|
+
const items = storage(itemsBuffer, "gslPrefixItems");
|
|
26487
|
+
const blockSums = storage(blockSumsBuffer, "gslPrefixBlockSums");
|
|
26491
26488
|
const temp = N$2.workgroupArray("uint", PREFIX_ITEMS_PER_WORKGROUP).setName(
|
|
26492
26489
|
"gslPrefixTemp"
|
|
26493
26490
|
);
|
|
@@ -26559,13 +26556,8 @@ function makePrefixScanTask(itemsBuffer, blockSumsBuffer, elementCount) {
|
|
|
26559
26556
|
return compute2;
|
|
26560
26557
|
}
|
|
26561
26558
|
function makePrefixAddTask(itemsBuffer, blockSumsBuffer, elementCount) {
|
|
26562
|
-
const items = storage(itemsBuffer, "
|
|
26563
|
-
const blockSums = storage(
|
|
26564
|
-
blockSumsBuffer,
|
|
26565
|
-
"uint",
|
|
26566
|
-
"gslPrefixBlockSums",
|
|
26567
|
-
true
|
|
26568
|
-
);
|
|
26559
|
+
const items = storage(itemsBuffer, "gslPrefixItems");
|
|
26560
|
+
const blockSums = storage(blockSumsBuffer, "gslPrefixBlockSums").toReadOnly();
|
|
26569
26561
|
const compute2 = N$2.Fn(() => {
|
|
26570
26562
|
const workgroup = workgroupIndex();
|
|
26571
26563
|
const first = workgroup.mul(PREFIX_ITEMS_PER_WORKGROUP).add(N$2.invocationLocalIndex.mul(2));
|
|
@@ -26595,23 +26587,15 @@ function makeReorderTask({
|
|
|
26595
26587
|
}) {
|
|
26596
26588
|
const inputKeys = storage(
|
|
26597
26589
|
inputKeysAttribute,
|
|
26598
|
-
"
|
|
26599
|
-
|
|
26600
|
-
|
|
26601
|
-
);
|
|
26602
|
-
const outputKeys = lastPass ? null : storage(outputKeysAttribute, "uint", "gslRadixOutputKeys");
|
|
26590
|
+
"gslRadixInputKeys"
|
|
26591
|
+
).toReadOnly();
|
|
26592
|
+
const outputKeys = lastPass ? null : storage(outputKeysAttribute, "gslRadixOutputKeys");
|
|
26603
26593
|
const inputValues = storage(
|
|
26604
26594
|
inputValuesAttribute,
|
|
26605
|
-
"
|
|
26606
|
-
|
|
26607
|
-
|
|
26608
|
-
);
|
|
26609
|
-
const outputValues = storage(
|
|
26610
|
-
outputValuesAttribute,
|
|
26611
|
-
"uint",
|
|
26612
|
-
"gslRadixOutputValues"
|
|
26613
|
-
);
|
|
26614
|
-
const prefix = storage(prefixAttribute, "uint", "gslRadixPrefix", true);
|
|
26595
|
+
"gslRadixInputValues"
|
|
26596
|
+
).toReadOnly();
|
|
26597
|
+
const outputValues = storage(outputValuesAttribute, "gslRadixOutputValues");
|
|
26598
|
+
const prefix = storage(prefixAttribute, "gslRadixPrefix").toReadOnly();
|
|
26615
26599
|
const bitOffsetNode = N$2.uint(bitOffset);
|
|
26616
26600
|
const digitMasks = N$2.workgroupArray("uint", RADIX_BUCKETS * 8).toAtomic().setName("gslRadixDigitMasks");
|
|
26617
26601
|
const digitOffsets = N$2.workgroupArray("uint", RADIX_BUCKETS).setName(
|
|
@@ -26734,7 +26718,7 @@ class WebGPURadixSort {
|
|
|
26734
26718
|
this.values = [makeBufferRef(safeCapacity), makeBufferRef(safeCapacity)];
|
|
26735
26719
|
const histogramCount = RADIX_BUCKETS * this.paddedWorkgroups(safeCapacity, ELEMENTS_PER_WORKGROUP);
|
|
26736
26720
|
this.blockSums = makeBufferRef(histogramCount);
|
|
26737
|
-
const counts = storage(this.counts, "
|
|
26721
|
+
const counts = storage(this.counts, "gslRadixCounts").toReadOnly();
|
|
26738
26722
|
this.createPrefixLevels(histogramCount, counts);
|
|
26739
26723
|
const setup = this.makeSetupTask(options.count);
|
|
26740
26724
|
const prefixNodes = this.prefixLevels.map((_, lastLevel) => {
|
|
@@ -26801,13 +26785,11 @@ class WebGPURadixSort {
|
|
|
26801
26785
|
return Math.max(1, width * Math.ceil(groups / this.maxWorkgroups));
|
|
26802
26786
|
}
|
|
26803
26787
|
makeSetupTask(gpuCount) {
|
|
26804
|
-
const counts = storage(this.counts, "
|
|
26788
|
+
const counts = storage(this.counts, "gslRadixCounts");
|
|
26805
26789
|
const dispatchBuffers = [
|
|
26806
26790
|
this.sortDispatch,
|
|
26807
26791
|
...this.prefixLevels.map((level) => level.dispatch)
|
|
26808
|
-
].map(
|
|
26809
|
-
(value, index2) => storage({ value }, "uint", `gslRadixDispatch${index2}`)
|
|
26810
|
-
);
|
|
26792
|
+
].map((value, index2) => storage({ value }, `gslRadixDispatch${index2}`));
|
|
26811
26793
|
const maxWorkgroups = N$2.uint(this.maxWorkgroups);
|
|
26812
26794
|
return N$2.Fn(() => {
|
|
26813
26795
|
const count = N$2.min(
|
|
@@ -26919,29 +26901,31 @@ class WebGPURadixSort {
|
|
|
26919
26901
|
const N$1 = TSL;
|
|
26920
26902
|
const WORKGROUP_SIZE = 256;
|
|
26921
26903
|
const PROJECT_SLOTS = 8;
|
|
26922
|
-
function buffer(
|
|
26923
|
-
return { value: new StorageBufferAttribute(new Uint32Array(
|
|
26904
|
+
function buffer(name) {
|
|
26905
|
+
return { value: new StorageBufferAttribute(new Uint32Array(1), 1), name };
|
|
26924
26906
|
}
|
|
26925
|
-
function bindBuffer(ref
|
|
26926
|
-
|
|
26927
|
-
return readOnly ? node.toReadOnly() : node;
|
|
26907
|
+
function bindBuffer(ref) {
|
|
26908
|
+
return N$1.storage(ref.value, "uint").setName(ref.name).onObjectUpdate(() => ref.value);
|
|
26928
26909
|
}
|
|
26929
26910
|
class ProjectedSplats {
|
|
26930
26911
|
constructor(renderer, uniforms) {
|
|
26931
26912
|
this.renderer = renderer;
|
|
26932
26913
|
this.uniforms = uniforms;
|
|
26933
|
-
this.indirect = new IndirectStorageBufferAttribute(
|
|
26934
|
-
|
|
26914
|
+
this.indirect = new IndirectStorageBufferAttribute(
|
|
26915
|
+
new Uint32Array([WEBGPU_SPLATS_PER_INSTANCE * 6, 0, 0, 0, 0]),
|
|
26916
|
+
1
|
|
26917
|
+
);
|
|
26935
26918
|
this.error = null;
|
|
26936
26919
|
this.cache = new ProjectionCache();
|
|
26937
26920
|
this.visibleCount = new StorageBufferAttribute(
|
|
26938
26921
|
new Uint32Array(1),
|
|
26939
26922
|
1
|
|
26940
26923
|
);
|
|
26941
|
-
this.keys = buffer();
|
|
26942
|
-
this.seeds = buffer();
|
|
26943
|
-
this.counts = buffer();
|
|
26924
|
+
this.keys = buffer("gslProjectionKeys");
|
|
26925
|
+
this.seeds = buffer("gslProjectionSeeds");
|
|
26926
|
+
this.counts = buffer("gslProjectionCounts");
|
|
26944
26927
|
this.slots = [];
|
|
26928
|
+
this.compiledSlots = 0;
|
|
26945
26929
|
this.matrix = new THREE__namespace.Matrix4();
|
|
26946
26930
|
this.translation = new THREE__namespace.Matrix4();
|
|
26947
26931
|
this.scale = new THREE__namespace.Vector3();
|
|
@@ -26971,8 +26955,8 @@ class ProjectedSplats {
|
|
|
26971
26955
|
const viewBase = uniformBinding(this.state, "viewBase", "uint");
|
|
26972
26956
|
const multiView = uniformBinding(this.state, "multiView", "bool");
|
|
26973
26957
|
const stochastic = uniformBinding(uniforms, "stochastic", "bool");
|
|
26974
|
-
const count = N$1.storage(this.visibleCount, "uint").toReadOnly().element(0);
|
|
26975
|
-
const seeds = bindBuffer(this.seeds
|
|
26958
|
+
const count = N$1.storage(this.visibleCount, "uint").setName("gslVisibleCount").toReadOnly().element(0);
|
|
26959
|
+
const seeds = bindBuffer(this.seeds).toReadOnly();
|
|
26976
26960
|
this.sorter = new WebGPURadixSort(1, this.keys, {
|
|
26977
26961
|
count,
|
|
26978
26962
|
storeOrder: (index2, value) => {
|
|
@@ -26989,15 +26973,8 @@ class ProjectedSplats {
|
|
|
26989
26973
|
this.limits.maxBufferSize
|
|
26990
26974
|
)
|
|
26991
26975
|
});
|
|
26992
|
-
const
|
|
26993
|
-
|
|
26994
|
-
draw.element(0).assign(WEBGPU_SPLATS_PER_INSTANCE * 6);
|
|
26995
|
-
draw.element(1).assign(0);
|
|
26996
|
-
draw.element(2).assign(0);
|
|
26997
|
-
draw.element(3).assign(0);
|
|
26998
|
-
draw.element(4).assign(0);
|
|
26999
|
-
})().compute(1).setName("Splat reset draw");
|
|
27000
|
-
const counter = N$1.storage(this.visibleCount, "uint").toAtomic();
|
|
26976
|
+
const drawCount = N$1.storage(this.indirect, "uint").setName("gslDrawIndirect").element(1);
|
|
26977
|
+
const counter = N$1.storage(this.visibleCount, "uint").setName("gslVisibleCount").toAtomic();
|
|
27001
26978
|
this.resetCount = N$1.Fn(() => {
|
|
27002
26979
|
N$1.atomicStore(counter.element(0), N$1.uint(0));
|
|
27003
26980
|
})().compute(1, [1]).setName("Splat reset visible count");
|
|
@@ -27008,7 +26985,9 @@ class ProjectedSplats {
|
|
|
27008
26985
|
N$1.If(index2.equal(0), () => {
|
|
27009
26986
|
counts.element(viewIndex).assign(count);
|
|
27010
26987
|
const instances = count.add(WEBGPU_SPLATS_PER_INSTANCE - 1).div(N$1.uint(WEBGPU_SPLATS_PER_INSTANCE));
|
|
27011
|
-
|
|
26988
|
+
drawCount.assign(
|
|
26989
|
+
N$1.select(viewIndex.equal(0), instances, drawCount.max(instances))
|
|
26990
|
+
);
|
|
27012
26991
|
});
|
|
27013
26992
|
N$1.If(multiView.and(stochastic).and(index2.lessThan(count)), () => {
|
|
27014
26993
|
this.cache.storeOrder(
|
|
@@ -27019,17 +26998,30 @@ class ProjectedSplats {
|
|
|
27019
26998
|
})().compute(1, [WORKGROUP_SIZE]).setName("Splat visible draw arguments");
|
|
27020
26999
|
for (let i = 0; i < PROJECT_SLOTS; i++) this.slots.push(this.createSlot());
|
|
27021
27000
|
this.ready = computeRenderer.compileComputeAsync([
|
|
27022
|
-
this.resetDraw,
|
|
27023
27001
|
this.resetCount,
|
|
27024
27002
|
this.finish,
|
|
27025
|
-
...this.
|
|
27026
|
-
|
|
27027
|
-
]).
|
|
27003
|
+
...this.sorter.nodes,
|
|
27004
|
+
this.slots[0].node
|
|
27005
|
+
]).then(() => {
|
|
27006
|
+
this.compiledSlots = 1;
|
|
27007
|
+
}).catch((error) => {
|
|
27028
27008
|
this.error = error;
|
|
27029
|
-
}).finally(() => {
|
|
27030
|
-
this.pending = false;
|
|
27031
|
-
if (this.disposed) this.disposeResources();
|
|
27032
27009
|
});
|
|
27010
|
+
this.compilation = this.ready.then(() => this.compileRemainingSlots());
|
|
27011
|
+
}
|
|
27012
|
+
async compileRemainingSlots() {
|
|
27013
|
+
if (this.disposed || this.compiledSlots === 0) return;
|
|
27014
|
+
try {
|
|
27015
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
27016
|
+
while (!this.disposed && this.compiledSlots < this.slots.length) {
|
|
27017
|
+
await this.renderer.compileComputeAsync([
|
|
27018
|
+
this.slots[this.compiledSlots].node
|
|
27019
|
+
]);
|
|
27020
|
+
this.compiledSlots++;
|
|
27021
|
+
}
|
|
27022
|
+
} catch (error) {
|
|
27023
|
+
this.error = error;
|
|
27024
|
+
}
|
|
27033
27025
|
}
|
|
27034
27026
|
createSlot() {
|
|
27035
27027
|
const uniforms = { ...this.state, ...makeGenerateUniforms() };
|
|
@@ -27053,7 +27045,7 @@ class ProjectedSplats {
|
|
|
27053
27045
|
const pixelScale = u("renderSize", "vec2").mul(u("focalAdjustment", "float")).mul(0.5);
|
|
27054
27046
|
const keys = bindBuffer(this.keys);
|
|
27055
27047
|
const seeds = bindBuffer(this.seeds);
|
|
27056
|
-
const counter = N$1.storage(this.visibleCount, "uint").toAtomic();
|
|
27048
|
+
const counter = N$1.storage(this.visibleCount, "uint").setName("gslVisibleCount").toAtomic();
|
|
27057
27049
|
const node = N$1.Fn(() => {
|
|
27058
27050
|
const index2 = N$1.uint(N$1.instanceIndex);
|
|
27059
27051
|
const generated = generate.prepare(index2);
|
|
@@ -27106,7 +27098,7 @@ class ProjectedSplats {
|
|
|
27106
27098
|
const stride = uniformBinding(this.state, "viewStride", "uint");
|
|
27107
27099
|
const base = eye.mul(stride).toVar();
|
|
27108
27100
|
const i = N$1.uint(N$1.instanceIndex).mul(WEBGPU_SPLATS_PER_INSTANCE).add(N$1.uint(N$1.positionGeometry.z));
|
|
27109
|
-
const counts = bindBuffer(this.counts
|
|
27101
|
+
const counts = bindBuffer(this.counts).toReadOnly();
|
|
27110
27102
|
const clipPosition = N$1.vec4(0, 0, 2, 1).toVar();
|
|
27111
27103
|
const rgba = N$1.vec4(0).toVar();
|
|
27112
27104
|
const splatUv = N$1.vec2(0).toVar();
|
|
@@ -27122,7 +27114,7 @@ class ProjectedSplats {
|
|
|
27122
27114
|
const cacheIndex = i.toVar();
|
|
27123
27115
|
const assignSeed = () => {
|
|
27124
27116
|
stochasticSeed.assign(
|
|
27125
|
-
multiView ? this.cache.readOrder(base.add(i)).y : bindBuffer(this.seeds
|
|
27117
|
+
multiView ? this.cache.readOrder(base.add(i)).y : bindBuffer(this.seeds).toReadOnly().element(i)
|
|
27126
27118
|
);
|
|
27127
27119
|
};
|
|
27128
27120
|
if (depthOnly) {
|
|
@@ -27201,7 +27193,7 @@ class ProjectedSplats {
|
|
|
27201
27193
|
}
|
|
27202
27194
|
render(accumulator, camera, geometry, radial, shrink = false) {
|
|
27203
27195
|
if (this.error) throw this.error;
|
|
27204
|
-
if (this.
|
|
27196
|
+
if (this.compiledSlots === 0 || this.disposed) {
|
|
27205
27197
|
geometry.instanceCount = 0;
|
|
27206
27198
|
return;
|
|
27207
27199
|
}
|
|
@@ -27235,12 +27227,11 @@ class ProjectedSplats {
|
|
|
27235
27227
|
this.scale
|
|
27236
27228
|
);
|
|
27237
27229
|
this.state.renderToViewScale.value = (this.scale.x + this.scale.y + this.scale.z) / 3;
|
|
27238
|
-
const pending =
|
|
27239
|
-
pending.push(this.resetCount);
|
|
27230
|
+
const pending = [this.resetCount];
|
|
27240
27231
|
let slotCount = 0;
|
|
27241
27232
|
for (const { node, count } of accumulator.mapping) {
|
|
27242
27233
|
if (!view.layers.test(node.layers)) continue;
|
|
27243
|
-
if (slotCount ===
|
|
27234
|
+
if (slotCount === this.compiledSlots) {
|
|
27244
27235
|
this.renderer.compute(pending);
|
|
27245
27236
|
pending.length = 0;
|
|
27246
27237
|
slotCount = 0;
|
|
@@ -27261,12 +27252,11 @@ class ProjectedSplats {
|
|
|
27261
27252
|
dispose() {
|
|
27262
27253
|
if (this.disposed) return;
|
|
27263
27254
|
this.disposed = true;
|
|
27264
|
-
|
|
27255
|
+
void this.compilation.then(() => this.disposeResources());
|
|
27265
27256
|
}
|
|
27266
27257
|
disposeResources() {
|
|
27267
27258
|
for (const slot of this.slots) slot.node.dispose();
|
|
27268
|
-
for (const node of [this.
|
|
27269
|
-
node.dispose();
|
|
27259
|
+
for (const node of [this.resetCount, this.finish]) node.dispose();
|
|
27270
27260
|
this.sorter.dispose();
|
|
27271
27261
|
this.cache.dispose();
|
|
27272
27262
|
this.visibleCount.dispose();
|