minecraft-renderer 0.1.107 → 0.1.108
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/dist/lightOwnerWorker.js +9 -0
- package/dist/lightOwnerWorker.js.map +7 -0
- package/dist/mesher.js +1 -1
- package/dist/mesher.js.map +2 -2
- package/dist/mesherWasm.js +222 -222
- package/dist/minecraft-renderer.js +80 -80
- package/dist/minecraft-renderer.js.meta.json +1 -1
- package/dist/threeWorker.js +513 -513
- package/package.json +1 -1
- package/src/bundler/bundlePrepare.test.ts +25 -0
- package/src/bundler/bundlePrepare.ts +1 -1
- package/src/graphicsBackend/appViewer.ts +15 -0
- package/src/graphicsBackend/config.ts +5 -0
- package/src/graphicsBackend/rendererDefaultOptions.test.ts +51 -0
- package/src/graphicsBackend/rendererDefaultOptions.ts +13 -1
- package/src/graphicsBackend/rendererOptionsSync.ts +3 -3
- package/src/lib/blockEntityLightRegistry.ts +4 -0
- package/src/lib/worldrendererCommon.lightOwner.test.ts +916 -0
- package/src/lib/worldrendererCommon.removeColumn.test.ts +16 -0
- package/src/lib/worldrendererCommon.ts +329 -67
- package/src/lighting/cache/rendererLightCache.test.ts +158 -0
- package/src/lighting/cache/rendererLightCache.ts +248 -0
- package/src/lighting/coordinator/clientLightCoordinator.test.ts +102 -0
- package/src/lighting/coordinator/clientLightCoordinator.ts +574 -0
- package/src/lighting/debug/clientLightProbe.test.ts +24 -0
- package/src/lighting/debug/clientLightProbe.ts +20 -0
- package/src/lighting/debug/clientLightTrace.test.ts +271 -0
- package/src/lighting/debug/clientLightTrace.ts +335 -0
- package/src/lighting/entity/entityLightController.test.ts +216 -0
- package/src/lighting/entity/entityLightController.ts +322 -0
- package/src/lighting/owner/clientLightOwner.test.ts +467 -0
- package/src/lighting/owner/clientLightOwner.ts +523 -0
- package/src/lighting/owner/lightOwnerHost.test.ts +78 -0
- package/src/lighting/owner/lightOwnerHost.ts +202 -0
- package/src/lighting/owner/lightOwnerWorker.ts +154 -0
- package/src/lighting/owner/updateLight1171OwnerPath.test.ts +68 -0
- package/src/lighting/owner/updateLightToOwnerEvents.ts +49 -0
- package/src/lighting/tables/lightTables1171.generated.json +10 -0
- package/src/lighting/tables/lightTables1171.test.ts +168 -0
- package/src/lighting/tables/lightTables1171.ts +59 -0
- package/src/lighting/versioning/clientLightVersions.test.ts +241 -0
- package/src/lighting/versioning/clientLightVersions.ts +203 -0
- package/src/lighting/versioning/pendingSectionFlush.test.ts +214 -0
- package/src/lighting/versioning/pendingSectionFlush.ts +123 -0
- package/src/mesher-shared/lightNibblePack.ts +26 -0
- package/src/mesher-shared/mcDataWorkerShim.cjs +9 -7
- package/src/mesher-shared/modelsGeometryCommon.test.ts +6 -1
- package/src/mesher-shared/shared.ts +48 -1
- package/src/three/chunkMeshManager.ts +26 -0
- package/src/three/entities.ts +77 -11
- package/src/three/entity/EntityMesh.ts +1 -0
- package/src/three/entity/legWalkAnimation.test.ts +11 -2
- package/src/three/entity/legWalkAnimation.ts +1 -9
- package/src/three/globalBlockBuffer.ts +94 -2
- package/src/three/globalLegacyBuffer.ts +94 -2
- package/src/three/graphicsBackendBase.ts +2 -9
- package/src/three/tests/globalBlockBuffer.test.ts +98 -0
- package/src/three/tests/globalLegacyBuffer.test.ts +108 -0
- package/src/three/worldRendererThree.ts +75 -34
- package/src/wasm-mesher/bridge/render-from-wasm.ts +46 -1
- package/src/wasm-mesher/runtime-build/wasm_mesher.d.ts +31 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher.js +180 -5
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm +0 -0
- package/src/wasm-mesher/runtime-build/wasm_mesher_bg.wasm.d.ts +11 -0
- package/src/wasm-mesher/tests/mesherLightDependencies.test.ts +83 -0
- package/src/wasm-mesher/tests/mesherWasmColumnTickSelect.test.ts +39 -0
- package/src/wasm-mesher/tests/mesherWasmConversionCache.test.ts +30 -1
- package/src/wasm-mesher/tests/mesherWasmTickParseCache.test.ts +73 -0
- package/src/wasm-mesher/tests/mesherWasmTickSchedule.test.ts +51 -0
- package/src/wasm-mesher/tests/mesherWasmTopologyPostCache.test.ts +78 -0
- package/src/wasm-mesher/worker/clientLightDarkPublication.probe.test.ts +32 -0
- package/src/wasm-mesher/worker/mesherLightDependencies.ts +43 -0
- package/src/wasm-mesher/worker/mesherWasm.ownerLightSync.test.ts +198 -0
- package/src/wasm-mesher/worker/mesherWasm.ts +506 -96
- package/src/wasm-mesher/worker/mesherWasmChunkBuffer.ts +1 -0
- package/src/wasm-mesher/worker/mesherWasmColumnTickSelect.ts +47 -0
- package/src/wasm-mesher/worker/mesherWasmConversionCache.ts +16 -5
- package/src/wasm-mesher/worker/mesherWasmLightDirty.test.ts +17 -1
- package/src/wasm-mesher/worker/mesherWasmLightDirty.ts +10 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.test.ts +208 -0
- package/src/wasm-mesher/worker/mesherWasmLightMerge.ts +228 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.test.ts +161 -0
- package/src/wasm-mesher/worker/mesherWasmOwnerLight.ts +112 -0
- package/src/wasm-mesher/worker/mesherWasmTickParseCache.ts +45 -0
- package/src/wasm-mesher/worker/mesherWasmTickSchedule.ts +44 -0
- package/src/wasm-mesher/worker/mesherWasmTopologyPostCache.ts +63 -0
- package/src/worldView/types.ts +1 -0
- package/src/worldView/worldView.geometryReload.test.ts +68 -0
- package/src/worldView/worldView.spiral.test.ts +1 -1
- package/src/worldView/worldView.ts +46 -1
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { VERTICES_PER_FACE, computeSectionOriginRel } from './shaders/cubeBlockShader'
|
|
13
13
|
import { computeCameraRelativeUniforms, type RenderOrigin } from './shaders/legacyBlockShader'
|
|
14
14
|
import { packWord2Empty } from '../wasm-mesher/bridge/shaderCubeBridge'
|
|
15
|
+
import { isClientLightTraceEnabled, recordClientLightTrace, recordClientLightTraceGpuSample } from '../lighting/debug/clientLightTrace'
|
|
15
16
|
|
|
16
17
|
type WebGLRendererInternals = THREE.WebGLRenderer & {
|
|
17
18
|
properties: {
|
|
@@ -162,9 +163,9 @@ export class GlobalBlockBuffer {
|
|
|
162
163
|
getSectionDrawStart(sectionKey: string): number | undefined {
|
|
163
164
|
const slot = this.sectionSlots.get(sectionKey)
|
|
164
165
|
if (!slot) return undefined
|
|
165
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
166
166
|
const replace = this.pendingReplace.get(sectionKey)
|
|
167
167
|
if (replace) return replace.oldStart
|
|
168
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
168
169
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
169
170
|
return slot.start
|
|
170
171
|
}
|
|
@@ -172,13 +173,37 @@ export class GlobalBlockBuffer {
|
|
|
172
173
|
getSectionDrawCount(sectionKey: string): number | undefined {
|
|
173
174
|
const slot = this.sectionSlots.get(sectionKey)
|
|
174
175
|
if (!slot) return undefined
|
|
175
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
176
176
|
const replace = this.pendingReplace.get(sectionKey)
|
|
177
177
|
if (replace) return replace.oldCount
|
|
178
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
178
179
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
179
180
|
return slot.count
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
getGpuSlotTrace(sectionKey: string):
|
|
184
|
+
| {
|
|
185
|
+
displayedStart?: number
|
|
186
|
+
displayedCount?: number
|
|
187
|
+
candidateStart: number
|
|
188
|
+
candidateCount: number
|
|
189
|
+
pendingReplace: boolean
|
|
190
|
+
pendingMove: boolean
|
|
191
|
+
unuploadedRanges: number
|
|
192
|
+
}
|
|
193
|
+
| undefined {
|
|
194
|
+
const slot = this.sectionSlots.get(sectionKey)
|
|
195
|
+
if (!slot) return undefined
|
|
196
|
+
return {
|
|
197
|
+
displayedStart: this.getSectionDrawStart(sectionKey),
|
|
198
|
+
displayedCount: this.getSectionDrawCount(sectionKey),
|
|
199
|
+
candidateStart: slot.start,
|
|
200
|
+
candidateCount: slot.count,
|
|
201
|
+
pendingReplace: this.pendingReplace.has(sectionKey),
|
|
202
|
+
pendingMove: this.pendingMove?.key === sectionKey,
|
|
203
|
+
unuploadedRanges: this.pendingRanges.length
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
182
207
|
getUploadEpoch(): number {
|
|
183
208
|
return this.uploadEpoch
|
|
184
209
|
}
|
|
@@ -282,6 +307,20 @@ export class GlobalBlockBuffer {
|
|
|
282
307
|
this.markDirty(slot.start, slot.start + faceCount - 1)
|
|
283
308
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
284
309
|
this.layoutVersion++
|
|
310
|
+
if (isClientLightTraceEnabled()) {
|
|
311
|
+
const gpu = this.getGpuSlotTrace(sectionKey)
|
|
312
|
+
recordClientLightTrace({
|
|
313
|
+
phase: 'gpuStaged',
|
|
314
|
+
sectionKey,
|
|
315
|
+
displayedStart: gpu?.displayedStart,
|
|
316
|
+
displayedCount: gpu?.displayedCount,
|
|
317
|
+
candidateStart: gpu?.candidateStart,
|
|
318
|
+
candidateCount: gpu?.candidateCount,
|
|
319
|
+
pendingReplace: gpu?.pendingReplace,
|
|
320
|
+
pendingMove: gpu?.pendingMove,
|
|
321
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
322
|
+
})
|
|
323
|
+
}
|
|
285
324
|
}
|
|
286
325
|
|
|
287
326
|
getLayoutVersion(): number {
|
|
@@ -388,6 +427,20 @@ export class GlobalBlockBuffer {
|
|
|
388
427
|
this.markDirty(newStart, newStart + section.count - 1)
|
|
389
428
|
this.pendingMove = { key: section.key, oldStart, newStart, count: section.count }
|
|
390
429
|
this.layoutVersion++
|
|
430
|
+
if (isClientLightTraceEnabled()) {
|
|
431
|
+
const gpu = this.getGpuSlotTrace(section.key)
|
|
432
|
+
recordClientLightTrace({
|
|
433
|
+
phase: 'gpuStaged',
|
|
434
|
+
sectionKey: section.key,
|
|
435
|
+
displayedStart: gpu?.displayedStart,
|
|
436
|
+
displayedCount: gpu?.displayedCount,
|
|
437
|
+
candidateStart: gpu?.candidateStart,
|
|
438
|
+
candidateCount: gpu?.candidateCount,
|
|
439
|
+
pendingReplace: gpu?.pendingReplace,
|
|
440
|
+
pendingMove: true,
|
|
441
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
442
|
+
})
|
|
443
|
+
}
|
|
391
444
|
}
|
|
392
445
|
|
|
393
446
|
uploadDirtyRange(): void {
|
|
@@ -411,6 +464,13 @@ export class GlobalBlockBuffer {
|
|
|
411
464
|
r.start = offset + count
|
|
412
465
|
}
|
|
413
466
|
this.uploadEpoch++
|
|
467
|
+
if (isClientLightTraceEnabled()) {
|
|
468
|
+
recordClientLightTraceGpuSample('gpuUploaded', this.highWatermark, () => ({
|
|
469
|
+
phase: 'gpuUploaded',
|
|
470
|
+
unuploadedRanges: this.pendingRanges.length,
|
|
471
|
+
drawableFaces: this.highWatermark
|
|
472
|
+
}))
|
|
473
|
+
}
|
|
414
474
|
}
|
|
415
475
|
|
|
416
476
|
setCameraOrigin(renderOrigin: RenderOrigin, x: number, y: number, z: number): void {
|
|
@@ -608,6 +668,9 @@ export class GlobalBlockBuffer {
|
|
|
608
668
|
private findMovableSection(maxCount: number): { key: string; start: number; count: number } | undefined {
|
|
609
669
|
const sections: Array<{ key: string; start: number; count: number }> = []
|
|
610
670
|
for (const [key, slot] of this.sectionSlots) {
|
|
671
|
+
if (this.pendingReplace.has(key)) continue
|
|
672
|
+
if (this.pendingMove?.key === key) continue
|
|
673
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) continue
|
|
611
674
|
sections.push({ key, start: slot.start, count: slot.count })
|
|
612
675
|
}
|
|
613
676
|
if (sections.length === 0) return undefined
|
|
@@ -684,11 +747,26 @@ export class GlobalBlockBuffer {
|
|
|
684
747
|
this.mesh.geometry.instanceCount = this.highWatermark
|
|
685
748
|
this.layoutVersion++
|
|
686
749
|
this.uploadEpoch++
|
|
750
|
+
if (isClientLightTraceEnabled()) {
|
|
751
|
+
const gpu = this.getGpuSlotTrace(key)
|
|
752
|
+
recordClientLightTrace({
|
|
753
|
+
phase: 'gpuCommitted',
|
|
754
|
+
sectionKey: key,
|
|
755
|
+
displayedStart: gpu?.displayedStart,
|
|
756
|
+
displayedCount: gpu?.displayedCount,
|
|
757
|
+
candidateStart: gpu?.candidateStart,
|
|
758
|
+
candidateCount: gpu?.candidateCount,
|
|
759
|
+
pendingReplace: false,
|
|
760
|
+
pendingMove: gpu?.pendingMove,
|
|
761
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
762
|
+
})
|
|
763
|
+
}
|
|
687
764
|
}
|
|
688
765
|
|
|
689
766
|
private finalizePendingMove(): void {
|
|
690
767
|
const move = this.pendingMove
|
|
691
768
|
if (!move) return
|
|
769
|
+
const movedKey = move.key
|
|
692
770
|
|
|
693
771
|
const { oldStart, count } = move
|
|
694
772
|
this.zeroAndFreeSlot(oldStart, count)
|
|
@@ -697,6 +775,20 @@ export class GlobalBlockBuffer {
|
|
|
697
775
|
this.pendingMove = null
|
|
698
776
|
this.layoutVersion++
|
|
699
777
|
this.uploadEpoch++
|
|
778
|
+
if (isClientLightTraceEnabled()) {
|
|
779
|
+
const gpu = this.getGpuSlotTrace(movedKey)
|
|
780
|
+
recordClientLightTrace({
|
|
781
|
+
phase: 'gpuCommitted',
|
|
782
|
+
sectionKey: movedKey,
|
|
783
|
+
displayedStart: gpu?.displayedStart,
|
|
784
|
+
displayedCount: gpu?.displayedCount,
|
|
785
|
+
candidateStart: gpu?.candidateStart,
|
|
786
|
+
candidateCount: gpu?.candidateCount,
|
|
787
|
+
pendingReplace: gpu?.pendingReplace,
|
|
788
|
+
pendingMove: false,
|
|
789
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
790
|
+
})
|
|
791
|
+
}
|
|
700
792
|
}
|
|
701
793
|
|
|
702
794
|
private shrinkHighWatermark(): void {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type LegacyMultiDrawScratch
|
|
11
11
|
} from './legacyMultiDraw'
|
|
12
12
|
import { computeCameraRelativeUniforms, type RenderOrigin } from './shaders/legacyBlockShader'
|
|
13
|
+
import { isClientLightTraceEnabled, recordClientLightTrace, recordClientLightTraceGpuSample } from '../lighting/debug/clientLightTrace'
|
|
13
14
|
|
|
14
15
|
const VERTS_PER_QUAD = 4
|
|
15
16
|
const INDICES_PER_QUAD = 6
|
|
@@ -337,6 +338,20 @@ export class GlobalLegacyBuffer {
|
|
|
337
338
|
this.markDirty(slot.start, slot.start + quadCount - 1)
|
|
338
339
|
this.syncDefaultDrawGroups()
|
|
339
340
|
this.layoutVersion++
|
|
341
|
+
if (isClientLightTraceEnabled()) {
|
|
342
|
+
const gpu = this.getGpuSlotTrace(sectionKey)
|
|
343
|
+
recordClientLightTrace({
|
|
344
|
+
phase: 'gpuStaged',
|
|
345
|
+
sectionKey,
|
|
346
|
+
displayedStart: gpu?.displayedStart,
|
|
347
|
+
displayedCount: gpu?.displayedCount,
|
|
348
|
+
candidateStart: gpu?.candidateStart,
|
|
349
|
+
candidateCount: gpu?.candidateCount,
|
|
350
|
+
pendingReplace: gpu?.pendingReplace,
|
|
351
|
+
pendingMove: gpu?.pendingMove,
|
|
352
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
353
|
+
})
|
|
354
|
+
}
|
|
340
355
|
return true
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -367,9 +382,9 @@ export class GlobalLegacyBuffer {
|
|
|
367
382
|
getSectionDrawStart(sectionKey: string): number | undefined {
|
|
368
383
|
const slot = this.sectionSlots.get(sectionKey)
|
|
369
384
|
if (!slot) return undefined
|
|
370
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
371
385
|
const replace = this.pendingReplace.get(sectionKey)
|
|
372
386
|
if (replace) return replace.oldStart
|
|
387
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.oldStart
|
|
373
388
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
374
389
|
return slot.start
|
|
375
390
|
}
|
|
@@ -377,13 +392,37 @@ export class GlobalLegacyBuffer {
|
|
|
377
392
|
getSectionDrawCount(sectionKey: string): number | undefined {
|
|
378
393
|
const slot = this.sectionSlots.get(sectionKey)
|
|
379
394
|
if (!slot) return undefined
|
|
380
|
-
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
381
395
|
const replace = this.pendingReplace.get(sectionKey)
|
|
382
396
|
if (replace) return replace.oldCount
|
|
397
|
+
if (this.pendingMove?.key === sectionKey) return this.pendingMove.count
|
|
383
398
|
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) return undefined
|
|
384
399
|
return slot.count
|
|
385
400
|
}
|
|
386
401
|
|
|
402
|
+
getGpuSlotTrace(sectionKey: string):
|
|
403
|
+
| {
|
|
404
|
+
displayedStart?: number
|
|
405
|
+
displayedCount?: number
|
|
406
|
+
candidateStart: number
|
|
407
|
+
candidateCount: number
|
|
408
|
+
pendingReplace: boolean
|
|
409
|
+
pendingMove: boolean
|
|
410
|
+
unuploadedRanges: number
|
|
411
|
+
}
|
|
412
|
+
| undefined {
|
|
413
|
+
const slot = this.sectionSlots.get(sectionKey)
|
|
414
|
+
if (!slot) return undefined
|
|
415
|
+
return {
|
|
416
|
+
displayedStart: this.getSectionDrawStart(sectionKey),
|
|
417
|
+
displayedCount: this.getSectionDrawCount(sectionKey),
|
|
418
|
+
candidateStart: slot.start,
|
|
419
|
+
candidateCount: slot.count,
|
|
420
|
+
pendingReplace: this.pendingReplace.has(sectionKey),
|
|
421
|
+
pendingMove: this.pendingMove?.key === sectionKey,
|
|
422
|
+
unuploadedRanges: this.pendingRanges.length
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
387
426
|
getPendingMove(): PendingMove | null {
|
|
388
427
|
return this.pendingMove
|
|
389
428
|
}
|
|
@@ -425,6 +464,20 @@ export class GlobalLegacyBuffer {
|
|
|
425
464
|
this.markDirty(newStart, newStart + section.count - 1)
|
|
426
465
|
this.pendingMove = { key: section.key, oldStart, newStart, count: section.count }
|
|
427
466
|
this.layoutVersion++
|
|
467
|
+
if (isClientLightTraceEnabled()) {
|
|
468
|
+
const gpu = this.getGpuSlotTrace(section.key)
|
|
469
|
+
recordClientLightTrace({
|
|
470
|
+
phase: 'gpuStaged',
|
|
471
|
+
sectionKey: section.key,
|
|
472
|
+
displayedStart: gpu?.displayedStart,
|
|
473
|
+
displayedCount: gpu?.displayedCount,
|
|
474
|
+
candidateStart: gpu?.candidateStart,
|
|
475
|
+
candidateCount: gpu?.candidateCount,
|
|
476
|
+
pendingReplace: gpu?.pendingReplace,
|
|
477
|
+
pendingMove: true,
|
|
478
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
479
|
+
})
|
|
480
|
+
}
|
|
428
481
|
}
|
|
429
482
|
|
|
430
483
|
updateDrawSpans(visible: VisibleSectionSpan[], mode: 'opaque' | 'sortedBlend'): void {
|
|
@@ -696,6 +749,13 @@ export class GlobalLegacyBuffer {
|
|
|
696
749
|
r.start = quadOffset + quadCount
|
|
697
750
|
}
|
|
698
751
|
this.uploadEpoch++
|
|
752
|
+
if (isClientLightTraceEnabled()) {
|
|
753
|
+
recordClientLightTraceGpuSample('gpuUploaded', this.highWatermark, () => ({
|
|
754
|
+
phase: 'gpuUploaded',
|
|
755
|
+
unuploadedRanges: this.pendingRanges.length,
|
|
756
|
+
drawableFaces: this.highWatermark
|
|
757
|
+
}))
|
|
758
|
+
}
|
|
699
759
|
}
|
|
700
760
|
|
|
701
761
|
uploadDirtyIndexRange(): void {
|
|
@@ -898,6 +958,9 @@ export class GlobalLegacyBuffer {
|
|
|
898
958
|
private findMovableSection(maxCount: number): { key: string; start: number; count: number } | undefined {
|
|
899
959
|
const sections: Array<{ key: string; start: number; count: number }> = []
|
|
900
960
|
for (const [key, slot] of this.sectionSlots) {
|
|
961
|
+
if (this.pendingReplace.has(key)) continue
|
|
962
|
+
if (this.pendingMove?.key === key) continue
|
|
963
|
+
if (!this.rangeFullyUploaded(slot.start, slot.start + slot.count - 1)) continue
|
|
901
964
|
sections.push({ key, start: slot.start, count: slot.count })
|
|
902
965
|
}
|
|
903
966
|
if (sections.length === 0) return undefined
|
|
@@ -1006,11 +1069,26 @@ export class GlobalLegacyBuffer {
|
|
|
1006
1069
|
this.syncDefaultDrawGroups()
|
|
1007
1070
|
this.layoutVersion++
|
|
1008
1071
|
this.uploadEpoch++
|
|
1072
|
+
if (isClientLightTraceEnabled()) {
|
|
1073
|
+
const gpu = this.getGpuSlotTrace(key)
|
|
1074
|
+
recordClientLightTrace({
|
|
1075
|
+
phase: 'gpuCommitted',
|
|
1076
|
+
sectionKey: key,
|
|
1077
|
+
displayedStart: gpu?.displayedStart,
|
|
1078
|
+
displayedCount: gpu?.displayedCount,
|
|
1079
|
+
candidateStart: gpu?.candidateStart,
|
|
1080
|
+
candidateCount: gpu?.candidateCount,
|
|
1081
|
+
pendingReplace: false,
|
|
1082
|
+
pendingMove: gpu?.pendingMove,
|
|
1083
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
1084
|
+
})
|
|
1085
|
+
}
|
|
1009
1086
|
}
|
|
1010
1087
|
|
|
1011
1088
|
private finalizePendingMove(): void {
|
|
1012
1089
|
const move = this.pendingMove
|
|
1013
1090
|
if (!move) return
|
|
1091
|
+
const movedKey = move.key
|
|
1014
1092
|
|
|
1015
1093
|
const { oldStart, count } = move
|
|
1016
1094
|
this.zeroAndFreeSlot(oldStart, count)
|
|
@@ -1019,6 +1097,20 @@ export class GlobalLegacyBuffer {
|
|
|
1019
1097
|
this.pendingMove = null
|
|
1020
1098
|
this.layoutVersion++
|
|
1021
1099
|
this.uploadEpoch++
|
|
1100
|
+
if (isClientLightTraceEnabled()) {
|
|
1101
|
+
const gpu = this.getGpuSlotTrace(movedKey)
|
|
1102
|
+
recordClientLightTrace({
|
|
1103
|
+
phase: 'gpuCommitted',
|
|
1104
|
+
sectionKey: movedKey,
|
|
1105
|
+
displayedStart: gpu?.displayedStart,
|
|
1106
|
+
displayedCount: gpu?.displayedCount,
|
|
1107
|
+
candidateStart: gpu?.candidateStart,
|
|
1108
|
+
candidateCount: gpu?.candidateCount,
|
|
1109
|
+
pendingReplace: gpu?.pendingReplace,
|
|
1110
|
+
pendingMove: false,
|
|
1111
|
+
unuploadedRanges: gpu?.unuploadedRanges
|
|
1112
|
+
})
|
|
1113
|
+
}
|
|
1022
1114
|
}
|
|
1023
1115
|
|
|
1024
1116
|
private growCapacity(minQuads: number): void {
|
|
@@ -87,15 +87,8 @@ export const getBackendMethods = (worldRenderer: WorldRendererThree): any => {
|
|
|
87
87
|
},
|
|
88
88
|
feedChunkPacket(payload: FeedChunkPacketPayload) {
|
|
89
89
|
// Forward parsed/raw map_chunk + update_light packets from the
|
|
90
|
-
// web-client to
|
|
91
|
-
|
|
92
|
-
// single recipient); useWorkerProxy still gives zero-copy transfer
|
|
93
|
-
// from main into the off-thread renderer worker for free.
|
|
94
|
-
const { kind, ...rest } = payload
|
|
95
|
-
const message = { type: kind, ...rest }
|
|
96
|
-
for (const worker of worldRenderer.workers) {
|
|
97
|
-
worker.postMessage(message)
|
|
98
|
-
}
|
|
90
|
+
// web-client to WASM mesher workers (and the light owner when flagged).
|
|
91
|
+
worldRenderer.feedChunkPacket(payload)
|
|
99
92
|
},
|
|
100
93
|
getChunksDebugState() {
|
|
101
94
|
const loadedSectionsChunks: Record<string, true> = {}
|
|
@@ -215,3 +215,101 @@ test('GlobalBlockBuffer: uploadEpoch increments when dirty range drains', () =>
|
|
|
215
215
|
buffer.dispose()
|
|
216
216
|
mat.dispose()
|
|
217
217
|
})
|
|
218
|
+
|
|
219
|
+
function faceWords(faceCount: number): Uint32Array {
|
|
220
|
+
return new Uint32Array(faceCount * 4)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function drawableCubeFaces(buffer: GlobalBlockBuffer, key: string): number {
|
|
224
|
+
const start = buffer.getSectionDrawStart(key)
|
|
225
|
+
const count = buffer.getSectionDrawCount(key)
|
|
226
|
+
if (start === undefined || count === undefined) return 0
|
|
227
|
+
const spans = buildVisibleCubeSpans([{ start, count }], buffer.getHighWatermark(), false, undefined, buffer.getPendingDirtyRanges())
|
|
228
|
+
return spans.reduce((sum, span) => sum + span.count, 0)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
test('GlobalBlockBuffer: compaction must not hide pending-replace displayed faces', () => {
|
|
232
|
+
const scene = new THREE.Scene()
|
|
233
|
+
const mat = createCubeBlockMaterial()
|
|
234
|
+
const buffer = new GlobalBlockBuffer(mat, scene)
|
|
235
|
+
|
|
236
|
+
buffer.addSection('H', faceWords(20_000), 20_000)
|
|
237
|
+
buffer.addSection('A', faceWords(8000), 8000)
|
|
238
|
+
buffer.addSection('B', faceWords(1000), 1000)
|
|
239
|
+
drainUploads(buffer)
|
|
240
|
+
const original = buffer.getSectionDrawStart('A')
|
|
241
|
+
|
|
242
|
+
buffer.addSection('A', faceWords(8000), 8000)
|
|
243
|
+
buffer.removeSection('H')
|
|
244
|
+
buffer.compactStep()
|
|
245
|
+
buffer.uploadDirtyRange()
|
|
246
|
+
|
|
247
|
+
expect(drawableCubeFaces(buffer, 'A')).toBe(8000)
|
|
248
|
+
expect(buffer.getSectionDrawStart('A')).toBe(original)
|
|
249
|
+
const move = buffer.getPendingMove()
|
|
250
|
+
expect(move?.key === 'A').toBe(false)
|
|
251
|
+
|
|
252
|
+
buffer.dispose()
|
|
253
|
+
mat.dispose()
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('GlobalBlockBuffer: 27 simultaneous remeshes keep 8000 faces on every frame', () => {
|
|
257
|
+
const scene = new THREE.Scene()
|
|
258
|
+
const mat = createCubeBlockMaterial()
|
|
259
|
+
const buffer = new GlobalBlockBuffer(mat, scene)
|
|
260
|
+
const n = 27
|
|
261
|
+
const faces = 8000
|
|
262
|
+
|
|
263
|
+
for (let i = 0; i < n; i++) buffer.addSection(String(i), faceWords(faces), faces)
|
|
264
|
+
drainUploads(buffer)
|
|
265
|
+
for (let i = 0; i < n; i++) buffer.addSection(String(i), faceWords(faces), faces)
|
|
266
|
+
|
|
267
|
+
for (let frame = 1; frame <= 120; frame++) {
|
|
268
|
+
buffer.compactStep()
|
|
269
|
+
buffer.uploadDirtyRange()
|
|
270
|
+
for (let i = 0; i < n; i++) {
|
|
271
|
+
expect(drawableCubeFaces(buffer, String(i)), `section ${i} on frame ${frame}`).toBe(faces)
|
|
272
|
+
}
|
|
273
|
+
if (!buffer.hasPendingUploads() && !buffer.hasPendingReplace() && !buffer.getPendingMove()) break
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
buffer.dispose()
|
|
277
|
+
mat.dispose()
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('GlobalBlockBuffer: remesh during move keeps original displayed slot', () => {
|
|
281
|
+
const scene = new THREE.Scene()
|
|
282
|
+
const mat = createCubeBlockMaterial()
|
|
283
|
+
const buffer = new GlobalBlockBuffer(mat, scene)
|
|
284
|
+
|
|
285
|
+
buffer.addSection('H', faceWords(20_000), 20_000)
|
|
286
|
+
buffer.addSection('B', faceWords(1000), 1000)
|
|
287
|
+
buffer.addSection('A', faceWords(8000), 8000)
|
|
288
|
+
drainUploads(buffer)
|
|
289
|
+
buffer.removeSection('H')
|
|
290
|
+
drainUploads(buffer)
|
|
291
|
+
buffer.compactStep()
|
|
292
|
+
expect(buffer.getPendingMove()?.key).toBe('A')
|
|
293
|
+
const displayed = buffer.getSectionDrawStart('A')
|
|
294
|
+
expect(displayed).toBeDefined()
|
|
295
|
+
|
|
296
|
+
buffer.addSection('A', faceWords(8000), 8000)
|
|
297
|
+
expect(buffer.getSectionDrawStart('A')).toBe(displayed)
|
|
298
|
+
expect(buffer.getPendingMove()).toBeNull()
|
|
299
|
+
expect(buffer.hasPendingReplace()).toBe(true)
|
|
300
|
+
expect(drawableCubeFaces(buffer, 'A')).toBe(8000)
|
|
301
|
+
|
|
302
|
+
buffer.compactStep()
|
|
303
|
+
buffer.uploadDirtyRange()
|
|
304
|
+
expect(buffer.getSectionDrawStart('A')).toBe(displayed)
|
|
305
|
+
expect(buffer.getPendingMove()?.key === 'A').toBe(false)
|
|
306
|
+
expect(drawableCubeFaces(buffer, 'A')).toBe(8000)
|
|
307
|
+
|
|
308
|
+
drainUploads(buffer)
|
|
309
|
+
buffer.compactStep()
|
|
310
|
+
finishCurrentMove(buffer)
|
|
311
|
+
expect(drawableCubeFaces(buffer, 'A')).toBe(8000)
|
|
312
|
+
|
|
313
|
+
buffer.dispose()
|
|
314
|
+
mat.dispose()
|
|
315
|
+
})
|
|
@@ -892,3 +892,111 @@ test('GlobalLegacyBuffer: blend reorder metadata survives compaction', () => {
|
|
|
892
892
|
buffer.dispose()
|
|
893
893
|
mat.dispose()
|
|
894
894
|
})
|
|
895
|
+
|
|
896
|
+
function quadGeometry(quadCount: number): ReturnType<typeof makeQuadGeometry> {
|
|
897
|
+
const indices = new Uint32Array(quadCount * 6)
|
|
898
|
+
for (let i = 0; i < quadCount; i++) {
|
|
899
|
+
const v = i * 4
|
|
900
|
+
indices.set([v, v + 1, v + 2, v, v + 2, v + 3], i * 6)
|
|
901
|
+
}
|
|
902
|
+
return {
|
|
903
|
+
positions: new Float32Array(quadCount * 12),
|
|
904
|
+
colors: new Float32Array(quadCount * 12),
|
|
905
|
+
skyLights: new Float32Array(quadCount * 4),
|
|
906
|
+
blockLights: new Float32Array(quadCount * 4),
|
|
907
|
+
uvs: new Float32Array(quadCount * 8),
|
|
908
|
+
indices
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function drawableLegacyIndices(buffer: GlobalLegacyBuffer, key: string): number {
|
|
913
|
+
buffer.updateDrawSpans([{ key, distSq: 0 }], 'opaque')
|
|
914
|
+
return buffer.getVisibleIndexSpans().reduce((sum, span) => sum + span.indexCount, 0)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
test('GlobalLegacyBuffer: compaction must not hide pending-replace displayed indices', () => {
|
|
918
|
+
const scene = new THREE.Scene()
|
|
919
|
+
const mat = createGlobalLegacyBlockMaterial()
|
|
920
|
+
const buffer = new GlobalLegacyBuffer(mat, scene)
|
|
921
|
+
|
|
922
|
+
buffer.addSection('H', quadGeometry(8000), 0, 0, 0)
|
|
923
|
+
buffer.addSection('A', quadGeometry(3000), 0, 0, 0)
|
|
924
|
+
buffer.addSection('B', quadGeometry(500), 0, 0, 0)
|
|
925
|
+
drainUploads(buffer)
|
|
926
|
+
const original = buffer.getSectionDrawStart('A')
|
|
927
|
+
|
|
928
|
+
buffer.addSection('A', quadGeometry(3000), 0, 0, 0)
|
|
929
|
+
buffer.removeSection('H')
|
|
930
|
+
buffer.compactStep()
|
|
931
|
+
buffer.uploadDirtyRange()
|
|
932
|
+
buffer.updateDrawSpans([{ key: 'A', distSq: 0 }], 'opaque')
|
|
933
|
+
|
|
934
|
+
expect(drawableLegacyIndices(buffer, 'A')).toBe(18_000)
|
|
935
|
+
expect(buffer.getSectionDrawStart('A')).toBe(original)
|
|
936
|
+
expect(buffer.getPendingMove()?.key === 'A').toBe(false)
|
|
937
|
+
|
|
938
|
+
buffer.dispose()
|
|
939
|
+
mat.dispose()
|
|
940
|
+
})
|
|
941
|
+
|
|
942
|
+
test('GlobalLegacyBuffer: 27 simultaneous remeshes keep displayed quads on every frame', () => {
|
|
943
|
+
const scene = new THREE.Scene()
|
|
944
|
+
const mat = createGlobalLegacyBlockMaterial()
|
|
945
|
+
const buffer = new GlobalLegacyBuffer(mat, scene)
|
|
946
|
+
const n = 27
|
|
947
|
+
const quads = 1000
|
|
948
|
+
const expectedIndices = quads * 6
|
|
949
|
+
|
|
950
|
+
for (let i = 0; i < n; i++) buffer.addSection(String(i), quadGeometry(quads), 0, 0, 0)
|
|
951
|
+
drainUploads(buffer)
|
|
952
|
+
for (let i = 0; i < n; i++) buffer.addSection(String(i), quadGeometry(quads), 0, 0, 0)
|
|
953
|
+
|
|
954
|
+
for (let frame = 1; frame <= 160; frame++) {
|
|
955
|
+
buffer.compactStep()
|
|
956
|
+
buffer.uploadDirtyRange()
|
|
957
|
+
for (let i = 0; i < n; i++) {
|
|
958
|
+
expect(drawableLegacyIndices(buffer, String(i)), `section ${i} on frame ${frame}`).toBe(expectedIndices)
|
|
959
|
+
}
|
|
960
|
+
if (!buffer.hasPendingUploads() && !buffer.hasPendingReplace() && !buffer.getPendingMove()) break
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
buffer.dispose()
|
|
964
|
+
mat.dispose()
|
|
965
|
+
})
|
|
966
|
+
|
|
967
|
+
test('GlobalLegacyBuffer: remesh during move keeps original displayed slot', () => {
|
|
968
|
+
const scene = new THREE.Scene()
|
|
969
|
+
const mat = createGlobalLegacyBlockMaterial()
|
|
970
|
+
const buffer = new GlobalLegacyBuffer(mat, scene)
|
|
971
|
+
|
|
972
|
+
buffer.addSection('H', quadGeometry(8000), 0, 0, 0)
|
|
973
|
+
buffer.addSection('B', quadGeometry(500), 0, 0, 0)
|
|
974
|
+
buffer.addSection('A', quadGeometry(3000), 0, 0, 0)
|
|
975
|
+
drainUploads(buffer)
|
|
976
|
+
buffer.removeSection('H')
|
|
977
|
+
drainUploads(buffer)
|
|
978
|
+
buffer.compactStep()
|
|
979
|
+
expect(buffer.getPendingMove()?.key).toBe('A')
|
|
980
|
+
const displayed = buffer.getSectionDrawStart('A')
|
|
981
|
+
expect(displayed).toBeDefined()
|
|
982
|
+
|
|
983
|
+
buffer.addSection('A', quadGeometry(3000), 0, 0, 0)
|
|
984
|
+
expect(buffer.getSectionDrawStart('A')).toBe(displayed)
|
|
985
|
+
expect(buffer.getPendingMove()).toBeNull()
|
|
986
|
+
expect(buffer.hasPendingReplace()).toBe(true)
|
|
987
|
+
expect(drawableLegacyIndices(buffer, 'A')).toBe(18_000)
|
|
988
|
+
|
|
989
|
+
buffer.compactStep()
|
|
990
|
+
buffer.uploadDirtyRange()
|
|
991
|
+
expect(buffer.getSectionDrawStart('A')).toBe(displayed)
|
|
992
|
+
expect(buffer.getPendingMove()?.key === 'A').toBe(false)
|
|
993
|
+
expect(drawableLegacyIndices(buffer, 'A')).toBe(18_000)
|
|
994
|
+
|
|
995
|
+
drainUploads(buffer)
|
|
996
|
+
buffer.compactStep()
|
|
997
|
+
finishCurrentMove(buffer)
|
|
998
|
+
expect(drawableLegacyIndices(buffer, 'A')).toBe(18_000)
|
|
999
|
+
|
|
1000
|
+
buffer.dispose()
|
|
1001
|
+
mat.dispose()
|
|
1002
|
+
})
|