@woosh/meep-engine 3.60.9 → 3.61.1
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 +90 -0
- package/package.json +1 -1
- package/src/engine/ecs/EntityManager.d.ts +3 -3
- package/src/engine/ecs/EntityManager.js +3 -3
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
- package/src/engine/graphics/ecs/decal/v2/Decal.d.ts +46 -2
- package/src/engine/graphics/ecs/decal/v2/Decal.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/Decal.js +74 -5
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +13 -1
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.d.ts +9 -0
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.d.ts.map +1 -0
- package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.js +37 -0
- package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DecalSystem.js +462 -447
- package/src/engine/graphics3/decal/DECAL_GPU_RECORD.d.ts +14 -0
- package/src/engine/graphics3/decal/DECAL_GPU_RECORD.d.ts.map +1 -1
- package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +53 -13
- package/src/engine/graphics3/decal/chunk_decal_record.d.ts +6 -1
- package/src/engine/graphics3/decal/chunk_decal_record.d.ts.map +1 -1
- package/src/engine/graphics3/decal/chunk_decal_record.js +28 -9
- package/src/engine/graphics3/decal/decal_gpu_records.d.ts +2 -1
- package/src/engine/graphics3/decal/decal_gpu_records.d.ts.map +1 -1
- package/src/engine/graphics3/decal/decal_gpu_records.js +26 -5
- package/src/engine/graphics3/decal/decal_material_samplers.d.ts +6 -0
- package/src/engine/graphics3/decal/decal_material_samplers.d.ts.map +1 -1
- package/src/engine/graphics3/decal/decal_material_samplers.js +13 -0
- package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +1 -1
- package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +1 -1
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts +5 -0
- package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_decal_composite.js +69 -28
- package/src/engine/network/NetworkSession.d.ts +3 -0
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +68 -10
- package/src/engine/network/README.md +6 -3
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +51 -5
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +308 -34
- package/src/engine/network/replication/Replicator.d.ts +8 -2
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +13 -4
- package/src/engine/network/sim/ActionLog.d.ts +16 -2
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +33 -2
- package/src/engine/network/sim/RewindEngine.d.ts +9 -0
- package/src/engine/network/sim/RewindEngine.d.ts.map +1 -1
- package/src/engine/network/sim/RewindEngine.js +15 -1
- package/src/engine/network/state/FrameBufferRing.d.ts +12 -5
- package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
- package/src/engine/network/state/FrameBufferRing.js +32 -6
- package/src/engine/network/time/TimeDilation.d.ts +30 -14
- package/src/engine/network/time/TimeDilation.d.ts.map +1 -1
- package/src/engine/network/time/TimeDilation.js +47 -17
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/generation/foliage/skeleton/ShootWoodHash.d.ts +3 -2
- package/src/generation/foliage/skeleton/ShootWoodHash.d.ts.map +1 -1
- package/src/generation/foliage/skeleton/ShootWoodHash.js +25 -2
- package/src/shade/renderer/buffer/graph_read_buffer.d.ts +3 -1
- package/src/shade/renderer/buffer/graph_read_buffer.d.ts.map +1 -1
- package/src/shade/renderer/buffer/graph_read_buffer.js +9 -6
- package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.d.ts +5 -0
- package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.d.ts.map +1 -1
- package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js +48 -43
- package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
- package/src/shade/renderer/material/GPUMaterialManager.js +117 -35
- package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts +2 -2
- package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
- package/src/shade/renderer/material/standard/fragment_gbuffer.js +10 -3
- package/src/shade/renderer/material/standard/material_gbuffer_vertex.d.ts +15 -3
- package/src/shade/renderer/material/standard/material_gbuffer_vertex.d.ts.map +1 -1
- package/src/shade/renderer/material/standard/material_gbuffer_vertex.js +25 -4
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +23 -5
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +294 -20
- package/src/shade/renderer/particles/resize/shader_particle_shrink_alive_list.d.ts +21 -0
- package/src/shade/renderer/particles/resize/shader_particle_shrink_alive_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/resize/shader_particle_shrink_alive_list.js +83 -0
- package/src/shade/renderer/particles/resize/shader_particle_shrink_free_list.d.ts +21 -0
- package/src/shade/renderer/particles/resize/shader_particle_shrink_free_list.d.ts.map +1 -0
- package/src/shade/renderer/particles/resize/shader_particle_shrink_free_list.js +64 -0
- package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts.map +1 -1
- package/src/shade/renderer/restir/di/chunk_restir_resolve.js +91 -94
- package/src/shade/renderer/restir/reservoir/chunk_rtxdi_stream_sample.js +1 -1
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.js +36 -21
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.js +64 -0
- package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_vertex_normal.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_vertex_normal.js +13 -18
- package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_vertex_normal.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_vertex_normal.js +28 -30
- package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_u32_to_vec2.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_u32_to_vec2.js +15 -16
- package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.d.ts +0 -1
- package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.js +15 -18
- package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_bitset_or.d.ts +32 -0
- package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_bitset_or.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_bitset_or.js +73 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +7 -3
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.d.ts +0 -2
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.d.ts.map +0 -1
- package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.js +0 -6
|
@@ -108,13 +108,16 @@ export class ActionLog {
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Open a frame for writing after the records it already holds, rather than over them. A frame
|
|
111
|
-
* the ring does not hold is opened empty, as {@link begin_frame} opens it
|
|
111
|
+
* the ring does not hold is opened empty, as {@link begin_frame} opens it — unless the log has
|
|
112
|
+
* moved past it, its slot holding a later frame: the later frame keeps its records, and what is
|
|
113
|
+
* written for this one is dropped at {@link end_frame}.
|
|
112
114
|
*
|
|
113
115
|
* For a writer adding to a frame someone else wrote: records arriving from a peer for a frame
|
|
114
116
|
* this peer has already simulated. Opened with {@link begin_frame} instead, they would erase
|
|
115
117
|
* this peer's own records for the frame — and with them the prior state a rewind needs to undo
|
|
116
118
|
* that input, so a replay would apply it a second time, and the outbound stream, which packs
|
|
117
|
-
* from this log, would lose it before the peer it was for had confirmed it.
|
|
119
|
+
* from this log, would lose it before the peer it was for had confirmed it. A frame the log has
|
|
120
|
+
* moved past would erase the later frame's records the same way.
|
|
118
121
|
*
|
|
119
122
|
* @param {number} frame absolute frame number
|
|
120
123
|
*/
|
|
@@ -127,6 +130,34 @@ export class ActionLog {
|
|
|
127
130
|
this.#current_frame = frame;
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Open a frame the log holds for writing from byte `offset` of what it holds: the records from
|
|
135
|
+
* there on are dropped, and what is written takes their place.
|
|
136
|
+
*
|
|
137
|
+
* For a writer that has taken records back off the world and writes them again — the frame's
|
|
138
|
+
* earlier records stay, the later ones are written over.
|
|
139
|
+
*
|
|
140
|
+
* @param {number} frame absolute frame number
|
|
141
|
+
* @param {number} offset a record boundary in the frame, at most its end
|
|
142
|
+
*/
|
|
143
|
+
resume_frame_at(frame, offset) {
|
|
144
|
+
if (this.#current_frame !== -1) {
|
|
145
|
+
throw new Error(`ActionLog.resume_frame_at: previous frame ${this.#current_frame} still open; call end_frame first`);
|
|
146
|
+
}
|
|
147
|
+
if (!this.#ring.has(frame)) {
|
|
148
|
+
throw new Error(`ActionLog.resume_frame_at: frame ${frame} is not present in the log`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const buffer = this.#ring.resume(frame);
|
|
152
|
+
if (offset > buffer.position) {
|
|
153
|
+
throw new Error(`ActionLog.resume_frame_at: offset ${offset} is past frame ${frame}'s end ${buffer.position}`);
|
|
154
|
+
}
|
|
155
|
+
buffer.position = offset;
|
|
156
|
+
|
|
157
|
+
this.#current_buffer = buffer;
|
|
158
|
+
this.#current_frame = frame;
|
|
159
|
+
}
|
|
160
|
+
|
|
130
161
|
/**
|
|
131
162
|
* Drop every record the log holds, keeping each frame it holds present and empty.
|
|
132
163
|
*
|
|
@@ -50,6 +50,15 @@ export class RewindEngine {
|
|
|
50
50
|
* @param {number} frame
|
|
51
51
|
*/
|
|
52
52
|
undo_frame(frame: number): void;
|
|
53
|
+
/**
|
|
54
|
+
* Undo the records of a frame that start at byte `from` or later, in reverse order — the ones
|
|
55
|
+
* written to the frame from there on — and leave the ones before it applied.
|
|
56
|
+
* Throws if the frame is not present in the log.
|
|
57
|
+
*
|
|
58
|
+
* @param {number} frame
|
|
59
|
+
* @param {number} from offset of a record boundary in the frame
|
|
60
|
+
*/
|
|
61
|
+
undo_frame_from(frame: number, from: number): void;
|
|
53
62
|
/**
|
|
54
63
|
* Undo every frame in `(target_frame, current_frame]` so that the world is
|
|
55
64
|
* left in start-of-`target_frame + 1` state. Equivalently: the world ends
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RewindEngine.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/sim/RewindEngine.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAeI;;;;;;OAMG;IACH,uDANW;QACV,UAAc,YAAY;QAC1B,KAAS,yBAAyB;QAClC,kBAAsB,8BAA8B;KACjD,EAiBH;IAdG;;OAEG;IACH,sBAA4B;IAE5B;;OAEG;IACH,8BAAkB;IAElB;;OAEG;IACH,gDAA4C;IAGhD;;;;;OAKG;IACH,kBAFW,MAAM,
|
|
1
|
+
{"version":3,"file":"RewindEngine.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/sim/RewindEngine.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAeI;;;;;;OAMG;IACH,uDANW;QACV,UAAc,YAAY;QAC1B,KAAS,yBAAyB;QAClC,kBAAsB,8BAA8B;KACjD,EAiBH;IAdG;;OAEG;IACH,sBAA4B;IAE5B;;OAEG;IACH,8BAAkB;IAElB;;OAEG;IACH,gDAA4C;IAGhD;;;;;OAKG;IACH,kBAFW,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,uBAHW,MAAM,QACN,MAAM,QA4BhB;IAED;;;;;;;;;OASG;IACH,yBAHW,MAAM,gBACN,MAAM,QAWhB;;CAuDJ"}
|
|
@@ -67,9 +67,22 @@ export class RewindEngine {
|
|
|
67
67
|
* @param {number} frame
|
|
68
68
|
*/
|
|
69
69
|
undo_frame(frame) {
|
|
70
|
+
this.undo_frame_from(frame, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Undo the records of a frame that start at byte `from` or later, in reverse order — the ones
|
|
75
|
+
* written to the frame from there on — and leave the ones before it applied.
|
|
76
|
+
* Throws if the frame is not present in the log.
|
|
77
|
+
*
|
|
78
|
+
* @param {number} frame
|
|
79
|
+
* @param {number} from offset of a record boundary in the frame
|
|
80
|
+
*/
|
|
81
|
+
undo_frame_from(frame, from) {
|
|
70
82
|
assert.isNonNegativeInteger(frame, 'frame');
|
|
83
|
+
assert.isNonNegativeInteger(from, 'from');
|
|
71
84
|
if (!this.action_log.has_frame(frame)) {
|
|
72
|
-
throw new Error(`RewindEngine
|
|
85
|
+
throw new Error(`RewindEngine: frame ${frame} is not present in the action log`);
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
// 1. First pass: walk forward to find record boundaries.
|
|
@@ -77,6 +90,7 @@ export class RewindEngine {
|
|
|
77
90
|
cursor.open(this.action_log, frame);
|
|
78
91
|
let record_count = 0;
|
|
79
92
|
while (cursor.next()) {
|
|
93
|
+
if (cursor.record_start < from) continue;
|
|
80
94
|
if (record_count >= this.#record_starts.length) {
|
|
81
95
|
this.#grow_record_starts();
|
|
82
96
|
}
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
* from that moment; its recorded length is what was last committed — 0 for a
|
|
22
22
|
* slot `open` claimed, the old end for a frame `resume` reopened — so a reader
|
|
23
23
|
* sees none of what is still being written. Owners that allow a frame to stay
|
|
24
|
-
* open across calls are responsible for saying so.
|
|
24
|
+
* open across calls are responsible for saying so. `resume` never claims a slot
|
|
25
|
+
* that holds a later frame: a frame the ring has moved past is written to a
|
|
26
|
+
* buffer the ring does not keep.
|
|
25
27
|
*
|
|
26
28
|
* @author Alex Goldring
|
|
27
29
|
* @copyright Company Named Limited (c) 2026
|
|
@@ -52,8 +54,12 @@ export class FrameBufferRing {
|
|
|
52
54
|
open(frame: number): BinaryBuffer;
|
|
53
55
|
/**
|
|
54
56
|
* Claim the slot for `frame` to write AFTER what it already holds: a slot holding `frame`
|
|
55
|
-
* hands back its buffer positioned at its committed end, and
|
|
56
|
-
* {@link open} claims it. {@link commit} then fixes the longer length.
|
|
57
|
+
* hands back its buffer positioned at its committed end, and a slot holding an earlier frame,
|
|
58
|
+
* or none, is claimed as {@link open} claims it. {@link commit} then fixes the longer length.
|
|
59
|
+
*
|
|
60
|
+
* A slot holding a later frame is not claimed: the ring has moved past `frame`, and claiming
|
|
61
|
+
* the slot would erase a frame newer than the one being written. What is written for `frame`
|
|
62
|
+
* goes to a buffer the ring does not keep, and `frame` stays absent.
|
|
57
63
|
*
|
|
58
64
|
* @param {number} frame absolute frame number
|
|
59
65
|
* @returns {BinaryBuffer} positioned at the frame's committed end, ready to be written
|
|
@@ -69,9 +75,10 @@ export class FrameBufferRing {
|
|
|
69
75
|
*/
|
|
70
76
|
clear(): void;
|
|
71
77
|
/**
|
|
72
|
-
* Record how far the writer got, fixing the frame's length for readers.
|
|
78
|
+
* Record how far the writer got, fixing the frame's length for readers. A frame the ring does
|
|
79
|
+
* not hold — one {@link resume} found it had moved past — keeps nothing.
|
|
73
80
|
*
|
|
74
|
-
* @param {number} frame absolute frame number, previously passed to {@link open}
|
|
81
|
+
* @param {number} frame absolute frame number, previously passed to {@link open} or {@link resume}
|
|
75
82
|
*/
|
|
76
83
|
commit(frame: number): void;
|
|
77
84
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FrameBufferRing.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/state/FrameBufferRing.js"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"FrameBufferRing.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/state/FrameBufferRing.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IA2CI;;OAEG;IACH,4DAFW;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAC,EAmB9E;IA5BD;;;;;OAKG;IACH,yBAFU,MAAM,CAED;IAwBf;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,YAAY,CAkBxB;IAED;;;;;;;;;;;OAWG;IACH,cAHW,MAAM,GACJ,YAAY,CA0BxB;IAED;;;OAGG;IACH,cAEC;IAED;;OAEG;IACH,cAGC;IAED;;;;;OAKG;IACH,cAFW,MAAM,QAUhB;IAED;;;;;OAKG;IACH,WAHW,MAAM,GACJ,OAAO,CAMnB;IAED;;;;;;OAMG;IACH,kBAHW,MAAM,GACJ,YAAY,CAYxB;IAED;;;;;;OAMG;IACH,qBAHW,MAAM,GACJ,MAAM,CAQlB;;CACJ;6BA7O4B,sCAAsC"}
|
|
@@ -24,7 +24,9 @@ import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
|
24
24
|
* from that moment; its recorded length is what was last committed — 0 for a
|
|
25
25
|
* slot `open` claimed, the old end for a frame `resume` reopened — so a reader
|
|
26
26
|
* sees none of what is still being written. Owners that allow a frame to stay
|
|
27
|
-
* open across calls are responsible for saying so.
|
|
27
|
+
* open across calls are responsible for saying so. `resume` never claims a slot
|
|
28
|
+
* that holds a later frame: a frame the ring has moved past is written to a
|
|
29
|
+
* buffer the ring does not keep.
|
|
28
30
|
*
|
|
29
31
|
* @author Alex Goldring
|
|
30
32
|
* @copyright Company Named Limited (c) 2026
|
|
@@ -57,6 +59,13 @@ export class FrameBufferRing {
|
|
|
57
59
|
*/
|
|
58
60
|
#write_ends;
|
|
59
61
|
|
|
62
|
+
/**
|
|
63
|
+
* What {@link resume} hands the writer of a frame the ring has moved past: rewound for each
|
|
64
|
+
* writer, never read and never kept.
|
|
65
|
+
* @type {BinaryBuffer}
|
|
66
|
+
*/
|
|
67
|
+
#discard = new BinaryBuffer();
|
|
68
|
+
|
|
60
69
|
/**
|
|
61
70
|
* Number of frames retained. Writing frame `f` evicts frame
|
|
62
71
|
* `f - frame_capacity`.
|
|
@@ -114,8 +123,12 @@ export class FrameBufferRing {
|
|
|
114
123
|
|
|
115
124
|
/**
|
|
116
125
|
* Claim the slot for `frame` to write AFTER what it already holds: a slot holding `frame`
|
|
117
|
-
* hands back its buffer positioned at its committed end, and
|
|
118
|
-
* {@link open} claims it. {@link commit} then fixes the longer length.
|
|
126
|
+
* hands back its buffer positioned at its committed end, and a slot holding an earlier frame,
|
|
127
|
+
* or none, is claimed as {@link open} claims it. {@link commit} then fixes the longer length.
|
|
128
|
+
*
|
|
129
|
+
* A slot holding a later frame is not claimed: the ring has moved past `frame`, and claiming
|
|
130
|
+
* the slot would erase a frame newer than the one being written. What is written for `frame`
|
|
131
|
+
* goes to a buffer the ring does not keep, and `frame` stays absent.
|
|
119
132
|
*
|
|
120
133
|
* @param {number} frame absolute frame number
|
|
121
134
|
* @returns {BinaryBuffer} positioned at the frame's committed end, ready to be written
|
|
@@ -125,7 +138,17 @@ export class FrameBufferRing {
|
|
|
125
138
|
|
|
126
139
|
const slot = frame % this.frame_capacity;
|
|
127
140
|
|
|
128
|
-
|
|
141
|
+
const occupant = this.#frame_numbers[slot];
|
|
142
|
+
|
|
143
|
+
if (occupant > frame) {
|
|
144
|
+
const discard = this.#discard;
|
|
145
|
+
|
|
146
|
+
discard.position = 0;
|
|
147
|
+
|
|
148
|
+
return discard;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (occupant !== frame) {
|
|
129
152
|
return this.open(frame);
|
|
130
153
|
}
|
|
131
154
|
|
|
@@ -153,15 +176,18 @@ export class FrameBufferRing {
|
|
|
153
176
|
}
|
|
154
177
|
|
|
155
178
|
/**
|
|
156
|
-
* Record how far the writer got, fixing the frame's length for readers.
|
|
179
|
+
* Record how far the writer got, fixing the frame's length for readers. A frame the ring does
|
|
180
|
+
* not hold — one {@link resume} found it had moved past — keeps nothing.
|
|
157
181
|
*
|
|
158
|
-
* @param {number} frame absolute frame number, previously passed to {@link open}
|
|
182
|
+
* @param {number} frame absolute frame number, previously passed to {@link open} or {@link resume}
|
|
159
183
|
*/
|
|
160
184
|
commit(frame) {
|
|
161
185
|
assert.isNonNegativeInteger(frame, 'frame');
|
|
162
186
|
|
|
163
187
|
const slot = frame % this.frame_capacity;
|
|
164
188
|
|
|
189
|
+
if (this.#frame_numbers[slot] !== frame) return;
|
|
190
|
+
|
|
165
191
|
this.#write_ends[slot] = this.#buffers[slot].position;
|
|
166
192
|
}
|
|
167
193
|
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* How fast a client runs, from where its input lands at the host.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
* buffer
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* inputs faster than the server consumes them), the server asks for stretching.
|
|
9
|
-
*
|
|
10
|
-
* This is the same primitive used in Overwatch ("time dilation") and Rocket
|
|
11
|
-
* League. Adjustment is bounded to a few percent so it's imperceptible.
|
|
12
|
-
*
|
|
13
|
-
* Output is a multiplier on tick duration:
|
|
4
|
+
* The host reports, each tick, how many frames ahead of its simulation the client's newest frame is:
|
|
5
|
+
* the depth of its input buffer for that client. The client wants it at {@link target_buffer_depth},
|
|
6
|
+
* deep enough that its input is there when the host needs it and no deeper, since every frame of it is
|
|
7
|
+
* a frame of latency. {@link compute} turns the depth into a multiplier on the client's tick duration:
|
|
14
8
|
* factor < 1.0 → run faster (smaller tick interval)
|
|
15
9
|
* factor > 1.0 → run slower (larger tick interval)
|
|
16
10
|
* factor = 1.0 → run at nominal rate
|
|
17
11
|
*
|
|
12
|
+
* Behind, and up to `max_dilation × T` frames ahead, an error is steered at up to {@link max_dilation}:
|
|
13
|
+
* a few percent, too little to see, which is what jitter and clock drift need. This is the primitive
|
|
14
|
+
* Overwatch and Rocket League call time dilation. A larger lead — the host lost time: a hidden or
|
|
15
|
+
* hitched host tab — is repaid in proportion to its size: the client runs at `1 / (1 + lead / T)` of
|
|
16
|
+
* its rate, which nears a standstill and never reaches it, so it keeps stepping and reading the host's
|
|
17
|
+
* reports. The reports are a round trip old, and a response in proportion to an error that old repays
|
|
18
|
+
* it without overshooting when its time constant is at least `e` times the delay, so `T` is
|
|
19
|
+
* `e × max_round_trip_frames` — 87 frames for a 32-frame round trip, which puts the steering's reach
|
|
20
|
+
* at 4.35 frames: on every round trip up to that one, a lead is repaid to the end the steering reaches
|
|
21
|
+
* on its own. Behind, the steering is all there is: running fast costs steps, and a client a whole
|
|
22
|
+
* window behind re-takes the host's time instead (`NetworkSession`).
|
|
23
|
+
*
|
|
18
24
|
* @author Alex Goldring
|
|
19
25
|
* @copyright Company Named Limited (c) 2026
|
|
20
26
|
*/
|
|
@@ -24,12 +30,18 @@ export class TimeDilation {
|
|
|
24
30
|
* target_buffer_depth?: number,
|
|
25
31
|
* max_dilation?: number,
|
|
26
32
|
* gain?: number,
|
|
27
|
-
*
|
|
33
|
+
* max_round_trip_frames: number,
|
|
34
|
+
* }} options
|
|
35
|
+
*
|
|
36
|
+
* `max_round_trip_frames`: the longest round trip, in frames, the client runs on — how old, at
|
|
37
|
+
* most, the reports this works from are. Its owners pass its action log's depth: on a longer round
|
|
38
|
+
* trip the host's frames land past the log.
|
|
28
39
|
*/
|
|
29
|
-
constructor({ target_buffer_depth, max_dilation, gain }?: {
|
|
40
|
+
constructor({ target_buffer_depth, max_dilation, gain, max_round_trip_frames }?: {
|
|
30
41
|
target_buffer_depth?: number;
|
|
31
42
|
max_dilation?: number;
|
|
32
43
|
gain?: number;
|
|
44
|
+
max_round_trip_frames: number;
|
|
33
45
|
});
|
|
34
46
|
/** @readonly */
|
|
35
47
|
readonly target_buffer_depth: number;
|
|
@@ -37,13 +49,17 @@ export class TimeDilation {
|
|
|
37
49
|
readonly max_dilation: number;
|
|
38
50
|
/** @readonly */
|
|
39
51
|
readonly gain: number;
|
|
52
|
+
/** @readonly */
|
|
53
|
+
readonly max_round_trip_frames: number;
|
|
40
54
|
/**
|
|
41
55
|
* Compute the dilation factor for a client whose current input buffer depth
|
|
42
56
|
* is `current_depth` (number of unconsumed input frames).
|
|
43
57
|
*
|
|
44
58
|
* @param {number} current_depth
|
|
45
|
-
* @returns {number} factor in `[1 - max_dilation, 1 + max_dilation
|
|
59
|
+
* @returns {number} factor in `[1 - max_dilation, ∞)`; at most `1 + max_dilation` for a lead the
|
|
60
|
+
* steering repays
|
|
46
61
|
*/
|
|
47
62
|
compute(current_depth: number): number;
|
|
63
|
+
#private;
|
|
48
64
|
}
|
|
49
65
|
//# sourceMappingURL=TimeDilation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeDilation.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/time/TimeDilation.js"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"TimeDilation.d.ts","sourceRoot":"","sources":["../../../../../src/engine/network/time/TimeDilation.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH;IAQI;;;;;;;;;;;OAWG;IACH,iFAXW;QACV,mBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,YAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,IAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,qBAAyB,EAAE,MAAM,CAAC;KAC/B,EA6BH;IAbG,gBAAgB;IAChB,qCAA8C;IAE9C,gBAAgB;IAChB,8BAAgC;IAEhC,gBAAgB;IAChB,sBAAgB;IAEhB,gBAAgB;IAChB,uCAAkD;IAKtD;;;;;;;OAOG;IACH,uBAJW,MAAM,GACJ,MAAM,CAWlB;;CACJ"}
|
|
@@ -2,39 +2,61 @@ import { assert } from "../../../core/assert.js";
|
|
|
2
2
|
import { clamp } from "../../../core/math/clamp.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* How fast a client runs, from where its input lands at the host.
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
* buffer
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* inputs faster than the server consumes them), the server asks for stretching.
|
|
12
|
-
*
|
|
13
|
-
* This is the same primitive used in Overwatch ("time dilation") and Rocket
|
|
14
|
-
* League. Adjustment is bounded to a few percent so it's imperceptible.
|
|
15
|
-
*
|
|
16
|
-
* Output is a multiplier on tick duration:
|
|
7
|
+
* The host reports, each tick, how many frames ahead of its simulation the client's newest frame is:
|
|
8
|
+
* the depth of its input buffer for that client. The client wants it at {@link target_buffer_depth},
|
|
9
|
+
* deep enough that its input is there when the host needs it and no deeper, since every frame of it is
|
|
10
|
+
* a frame of latency. {@link compute} turns the depth into a multiplier on the client's tick duration:
|
|
17
11
|
* factor < 1.0 → run faster (smaller tick interval)
|
|
18
12
|
* factor > 1.0 → run slower (larger tick interval)
|
|
19
13
|
* factor = 1.0 → run at nominal rate
|
|
20
14
|
*
|
|
15
|
+
* Behind, and up to `max_dilation × T` frames ahead, an error is steered at up to {@link max_dilation}:
|
|
16
|
+
* a few percent, too little to see, which is what jitter and clock drift need. This is the primitive
|
|
17
|
+
* Overwatch and Rocket League call time dilation. A larger lead — the host lost time: a hidden or
|
|
18
|
+
* hitched host tab — is repaid in proportion to its size: the client runs at `1 / (1 + lead / T)` of
|
|
19
|
+
* its rate, which nears a standstill and never reaches it, so it keeps stepping and reading the host's
|
|
20
|
+
* reports. The reports are a round trip old, and a response in proportion to an error that old repays
|
|
21
|
+
* it without overshooting when its time constant is at least `e` times the delay, so `T` is
|
|
22
|
+
* `e × max_round_trip_frames` — 87 frames for a 32-frame round trip, which puts the steering's reach
|
|
23
|
+
* at 4.35 frames: on every round trip up to that one, a lead is repaid to the end the steering reaches
|
|
24
|
+
* on its own. Behind, the steering is all there is: running fast costs steps, and a client a whole
|
|
25
|
+
* window behind re-takes the host's time instead (`NetworkSession`).
|
|
26
|
+
*
|
|
21
27
|
* @author Alex Goldring
|
|
22
28
|
* @copyright Company Named Limited (c) 2026
|
|
23
29
|
*/
|
|
24
30
|
export class TimeDilation {
|
|
25
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The time constant, in frames, at which a lead is repaid: `e` of the longest round trips.
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
#repay_frames;
|
|
37
|
+
|
|
26
38
|
/**
|
|
27
39
|
* @param {{
|
|
28
40
|
* target_buffer_depth?: number,
|
|
29
41
|
* max_dilation?: number,
|
|
30
42
|
* gain?: number,
|
|
31
|
-
*
|
|
43
|
+
* max_round_trip_frames: number,
|
|
44
|
+
* }} options
|
|
45
|
+
*
|
|
46
|
+
* `max_round_trip_frames`: the longest round trip, in frames, the client runs on — how old, at
|
|
47
|
+
* most, the reports this works from are. Its owners pass its action log's depth: on a longer round
|
|
48
|
+
* trip the host's frames land past the log.
|
|
32
49
|
*/
|
|
33
|
-
constructor({ target_buffer_depth = 2, max_dilation = 0.05, gain = 0.05 } = {}) {
|
|
50
|
+
constructor({ target_buffer_depth = 2, max_dilation = 0.05, gain = 0.05, max_round_trip_frames } = {}) {
|
|
34
51
|
assert.isNumber(target_buffer_depth, 'target_buffer_depth');
|
|
35
52
|
assert.isNumber(max_dilation, 'max_dilation');
|
|
36
53
|
assert.isNumber(gain, 'gain');
|
|
37
54
|
|
|
55
|
+
// A real check: NaN here reaches the engine's fixed-step time scale and stops the client.
|
|
56
|
+
if (!(max_round_trip_frames > 0 && Number.isFinite(max_round_trip_frames))) {
|
|
57
|
+
throw new Error(`TimeDilation: max_round_trip_frames must be a positive number of frames, got ${max_round_trip_frames}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
38
60
|
/** @readonly */
|
|
39
61
|
this.target_buffer_depth = target_buffer_depth;
|
|
40
62
|
|
|
@@ -43,6 +65,11 @@ export class TimeDilation {
|
|
|
43
65
|
|
|
44
66
|
/** @readonly */
|
|
45
67
|
this.gain = gain;
|
|
68
|
+
|
|
69
|
+
/** @readonly */
|
|
70
|
+
this.max_round_trip_frames = max_round_trip_frames;
|
|
71
|
+
|
|
72
|
+
this.#repay_frames = Math.E * max_round_trip_frames;
|
|
46
73
|
}
|
|
47
74
|
|
|
48
75
|
/**
|
|
@@ -50,13 +77,16 @@ export class TimeDilation {
|
|
|
50
77
|
* is `current_depth` (number of unconsumed input frames).
|
|
51
78
|
*
|
|
52
79
|
* @param {number} current_depth
|
|
53
|
-
* @returns {number} factor in `[1 - max_dilation, 1 + max_dilation
|
|
80
|
+
* @returns {number} factor in `[1 - max_dilation, ∞)`; at most `1 + max_dilation` for a lead the
|
|
81
|
+
* steering repays
|
|
54
82
|
*/
|
|
55
83
|
compute(current_depth) {
|
|
56
84
|
assert.isNumber(current_depth, 'current_depth');
|
|
85
|
+
|
|
57
86
|
// depth > target ⇒ slow client down (factor > 1). depth < target ⇒ speed up (factor < 1).
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
87
|
+
const error = current_depth - this.target_buffer_depth;
|
|
88
|
+
const steered = 1.0 + clamp(error * this.gain, -this.max_dilation, this.max_dilation);
|
|
89
|
+
|
|
90
|
+
return error > 0 ? Math.max(steered, 1.0 + error / this.#repay_frames) : steered;
|
|
61
91
|
}
|
|
62
92
|
}
|
|
@@ -66,11 +66,11 @@ export class FluidObstacleSystem extends System<any> {
|
|
|
66
66
|
/**
|
|
67
67
|
* @param {FluidComponent} fluid
|
|
68
68
|
*/
|
|
69
|
-
static "__#
|
|
69
|
+
static "__#486@#refresh_masks"(fluid: FluidComponent): void;
|
|
70
70
|
/**
|
|
71
71
|
* @param {FluidComponent} fluid
|
|
72
72
|
*/
|
|
73
|
-
static "__#
|
|
73
|
+
static "__#486@#clear_field"(fluid: FluidComponent): void;
|
|
74
74
|
/**
|
|
75
75
|
* Mark every cell of `fluid` whose centre lies within `inflation` of the
|
|
76
76
|
* posed shape as solid. Iteration is clipped to the shape's world AABB
|
|
@@ -82,7 +82,7 @@ export class FluidObstacleSystem extends System<any> {
|
|
|
82
82
|
* @param {number} inflation world-units SDF threshold
|
|
83
83
|
* @param {Float64Array} point length-3 scratch
|
|
84
84
|
*/
|
|
85
|
-
static "__#
|
|
85
|
+
static "__#486@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
|
|
86
86
|
/**
|
|
87
87
|
* Write the obstacle's translation velocity onto every face of every cell
|
|
88
88
|
* it voxelized — the moving-wall boundary condition. Runs AFTER the mask
|
|
@@ -102,7 +102,7 @@ export class FluidObstacleSystem extends System<any> {
|
|
|
102
102
|
* @param {number} wvy
|
|
103
103
|
* @param {number} wvz
|
|
104
104
|
*/
|
|
105
|
-
static "__#
|
|
105
|
+
static "__#486@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
|
|
106
106
|
constructor();
|
|
107
107
|
dependencies: (typeof FluidObstacle)[];
|
|
108
108
|
components_used: (ResourceAccessSpecification<typeof Transform64> | ResourceAccessSpecification<typeof FluidObstacle> | ResourceAccessSpecification<typeof Collider> | ResourceAccessSpecification<typeof RigidBody> | ResourceAccessSpecification<typeof FluidComponent>)[];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* wood, and thinner wood that thicker wood has grown into can be found. A unit is added once, when it has grown, under
|
|
4
4
|
* the cells its axis line crosses; a query reads every unit's place and radius from its axis as they are then, and
|
|
5
5
|
* reaches as far round as the thickest wood ({@link max_radius}), so wood that thickens needs no re-adding. The units
|
|
6
|
-
* of an axis that has died are passed over.
|
|
6
|
+
* of an axis that has died are passed over, and leave the hash at the next {@link refresh}.
|
|
7
7
|
*/
|
|
8
8
|
export class ShootWoodHash {
|
|
9
9
|
/**
|
|
@@ -69,7 +69,8 @@ export class ShootWoodHash {
|
|
|
69
69
|
add(m: number, k: number): void;
|
|
70
70
|
/**
|
|
71
71
|
* Records each cell's thickest living unit, after the wood has thickened, and the thickest of all
|
|
72
|
-
* ({@link max_radius}).
|
|
72
|
+
* ({@link max_radius}). A dead axis does not live again, so its units leave their cells, in the order the rest
|
|
73
|
+
* were added, and a cell left empty leaves the hash.
|
|
73
74
|
*/
|
|
74
75
|
refresh(): void;
|
|
75
76
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShootWoodHash.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootWoodHash.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ShootWoodHash.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootWoodHash.js"],"names":[],"mappings":"AAiCA;;;;;;GAMG;AACH;IA4GI;;;;;;OAMG;IACH,uBALW;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,KAClC,MAAM,KACN,MAAM,GACJ,MAAM,CAMlB;IAlFD;;OAEG;IACH,kBAFW,MAAM;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,CAAC,EAIrF;IAzCD,oCAAoC;IACpC,OADW,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC,CACd;IAElB;;;;OAIG;IACH,UAFU,IAAI,MAAM,EAAE,MAAM,CAAC,CAER;IAErB;;;OAGG;IACH,YAFU,MAAM,CAED;IAEf;;;OAGG;IACH,WAFU,MAAM,EAAE,CAEH;IAEf;;;OAGG;IACH,YAFU,MAAM,EAAE,CAEF;IAEhB;;;OAGG;IACH,MAFU,MAAM,EAAE,CAER;IAEV,cAAU;IAMN;eAHqB,OAAO;gBAAU,MAAM,EAAE;eAAS,MAAM,EAAE;eAAS,MAAM,EAAE;QAGhE;IAGpB;;;;OAIG;IACH,OAHW,MAAM,KACN,MAAM,QA2BhB;IAED;;;;OAIG;IACH,gBAyBC;IAeD;;;;;;;;;;;OAWG;IACH,QAPW,UAAU,MAAM,CAAC,KACjB,UAAU,MAAM,CAAC,UACjB,MAAM,cACN,MAAM,eACG,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,KAAG,OAAO,GAC/C,MAAM,CA6ClB;CACJ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { aabb3_intersects_segment3 } from "../../../core/geom/3d/aabb/aabb3_intersects_segment3.js";
|
|
1
2
|
import { line3_compute_segment_segment_closest_points } from "../../../core/geom/3d/line/line3_compute_segment_segment_closest_points.js";
|
|
2
3
|
import { shoot_radius } from "./shoot_geometry.js";
|
|
3
4
|
|
|
@@ -20,6 +21,14 @@ const CLEARANCE = 0.001;
|
|
|
20
21
|
*/
|
|
21
22
|
const KEY_SPAN = 2048;
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Margin (m) a cell is widened by when asking whether an axis line crosses it: far above the rounding of a point at
|
|
26
|
+
* the key's 205 m span, far below any wood, so a line along a cell's face or through its edge is under the cells either
|
|
27
|
+
* side of it.
|
|
28
|
+
* @type {number}
|
|
29
|
+
*/
|
|
30
|
+
const CROSSING_TOLERANCE = WOOD_CELL * 2 ** -20;
|
|
31
|
+
|
|
23
32
|
const SCRATCH_ST = new Float64Array(2);
|
|
24
33
|
|
|
25
34
|
/**
|
|
@@ -27,7 +36,7 @@ const SCRATCH_ST = new Float64Array(2);
|
|
|
27
36
|
* wood, and thinner wood that thicker wood has grown into can be found. A unit is added once, when it has grown, under
|
|
28
37
|
* the cells its axis line crosses; a query reads every unit's place and radius from its axis as they are then, and
|
|
29
38
|
* reaches as far round as the thickest wood ({@link max_radius}), so wood that thickens needs no re-adding. The units
|
|
30
|
-
* of an axis that has died are passed over.
|
|
39
|
+
* of an axis that has died are passed over, and leave the hash at the next {@link refresh}.
|
|
31
40
|
*/
|
|
32
41
|
export class ShootWoodHash {
|
|
33
42
|
/** @type {Map<number, number[]>} */
|
|
@@ -89,6 +98,11 @@ export class ShootWoodHash {
|
|
|
89
98
|
const y0 = Math.floor(Math.min(p[a + 1], p[b + 1]) / WOOD_CELL), y1 = Math.floor(Math.max(p[a + 1], p[b + 1]) / WOOD_CELL);
|
|
90
99
|
const z0 = Math.floor(Math.min(p[a + 2], p[b + 2]) / WOOD_CELL), z1 = Math.floor(Math.max(p[a + 2], p[b + 2]) / WOOD_CELL);
|
|
91
100
|
for (let x = x0; x <= x1; x++) for (let y = y0; y <= y1; y++) for (let z = z0; z <= z1; z++) {
|
|
101
|
+
if (!aabb3_intersects_segment3(
|
|
102
|
+
x * WOOD_CELL - CROSSING_TOLERANCE, y * WOOD_CELL - CROSSING_TOLERANCE, z * WOOD_CELL - CROSSING_TOLERANCE,
|
|
103
|
+
(x + 1) * WOOD_CELL + CROSSING_TOLERANCE, (y + 1) * WOOD_CELL + CROSSING_TOLERANCE, (z + 1) * WOOD_CELL + CROSSING_TOLERANCE,
|
|
104
|
+
p[a], p[a + 1], p[a + 2], p[b], p[b + 1], p[b + 2]
|
|
105
|
+
)) continue;
|
|
92
106
|
const key = ((x + KEY_SPAN / 2) * KEY_SPAN + (y + KEY_SPAN / 2)) * KEY_SPAN + (z + KEY_SPAN / 2);
|
|
93
107
|
let list = this.cells.get(key);
|
|
94
108
|
if (list === undefined) {
|
|
@@ -102,21 +116,30 @@ export class ShootWoodHash {
|
|
|
102
116
|
|
|
103
117
|
/**
|
|
104
118
|
* Records each cell's thickest living unit, after the wood has thickened, and the thickest of all
|
|
105
|
-
* ({@link max_radius}).
|
|
119
|
+
* ({@link max_radius}). A dead axis does not live again, so its units leave their cells, in the order the rest
|
|
120
|
+
* were added, and a cell left empty leaves the hash.
|
|
106
121
|
*/
|
|
107
122
|
refresh() {
|
|
108
123
|
const axes = this.axes;
|
|
109
124
|
let most = 0;
|
|
110
125
|
for (const [key, list] of this.cells) {
|
|
111
126
|
let max = 0;
|
|
127
|
+
let kept = 0;
|
|
112
128
|
for (let i = 0; i < list.length; i++) {
|
|
113
129
|
const id = list[i];
|
|
114
130
|
const axis = axes[this.unit_axis[id]];
|
|
115
131
|
if (!axis.alive) continue;
|
|
132
|
+
list[kept++] = id;
|
|
116
133
|
const k = this.unit_index[id];
|
|
117
134
|
const thickest = axis.radii.length > k + 1 ? Math.max(axis.radii[k], axis.radii[k + 1]) : shoot_radius(axis.units[k], 0);
|
|
118
135
|
if (thickest > max) max = thickest;
|
|
119
136
|
}
|
|
137
|
+
if (kept === 0) {
|
|
138
|
+
this.cells.delete(key);
|
|
139
|
+
this.cell_max.delete(key);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
list.length = kept;
|
|
120
143
|
this.cell_max.set(key, max);
|
|
121
144
|
if (max > most) most = max;
|
|
122
145
|
}
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* @param {number} buffer
|
|
6
6
|
* @param {number} [offset=0]
|
|
7
7
|
* @param {number} [size] defaults to buffer.size - offset
|
|
8
|
-
* @param {number} [timeout] in milliseconds, 0 for no timeout
|
|
8
|
+
* @param {number} [timeout] in milliseconds, 0 for no timeout: how long the read waits for its pass to run, starting
|
|
9
|
+
* now. A graph that is never run, or stops at a throwing pass before this one, rejects the read here. Once the pass has
|
|
10
|
+
* run, the read waits for the device however far behind it is, and settles when the mapping does.
|
|
9
11
|
* @returns {Promise<Reference<ArrayBuffer>>} don't forget to release the reference
|
|
10
12
|
*/
|
|
11
13
|
export function graph_read_buffer({ graph, buffer, offset, size, timeout }: FrameGraph): Promise<Reference<ArrayBuffer>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_read_buffer.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/graph_read_buffer.js"],"names":[],"mappings":"AAOA
|
|
1
|
+
{"version":3,"file":"graph_read_buffer.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/graph_read_buffer.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;GAWG;AACH,yFAFa,QAAQ,UAAU,WAAW,CAAC,CAAC,CAwH3C;0BAvIyB,2CAA2C"}
|
|
@@ -12,7 +12,9 @@ const scratch_buffer_descriptor = new BufferResourceDescriptor();
|
|
|
12
12
|
* @param {number} buffer
|
|
13
13
|
* @param {number} [offset=0]
|
|
14
14
|
* @param {number} [size] defaults to buffer.size - offset
|
|
15
|
-
* @param {number} [timeout] in milliseconds, 0 for no timeout
|
|
15
|
+
* @param {number} [timeout] in milliseconds, 0 for no timeout: how long the read waits for its pass to run, starting
|
|
16
|
+
* now. A graph that is never run, or stops at a throwing pass before this one, rejects the read here. Once the pass has
|
|
17
|
+
* run, the read waits for the device however far behind it is, and settles when the mapping does.
|
|
16
18
|
* @returns {Promise<Reference<ArrayBuffer>>} don't forget to release the reference
|
|
17
19
|
*/
|
|
18
20
|
export function graph_read_buffer({
|
|
@@ -47,6 +49,12 @@ export function graph_read_buffer({
|
|
|
47
49
|
* @param {GraphicsExecutionContext} context
|
|
48
50
|
*/
|
|
49
51
|
function execute(data, resources, context) {
|
|
52
|
+
// the copy is recorded: the mapping lands, or is refused, however long the device takes
|
|
53
|
+
if (timeout_handle !== -1) {
|
|
54
|
+
clearTimeout(timeout_handle);
|
|
55
|
+
timeout_handle = -1;
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
/**
|
|
51
59
|
* @type {GPUBuffer}
|
|
52
60
|
*/
|
|
@@ -94,11 +102,6 @@ export function graph_read_buffer({
|
|
|
94
102
|
return;
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
if (timeout_handle !== -1) {
|
|
98
|
-
clearTimeout(timeout_handle);
|
|
99
|
-
timeout_handle = -1;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
105
|
const data = destination.getMappedRange(0, _size);
|
|
103
106
|
|
|
104
107
|
ref = new Reference();
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shader is useful for copying buffers where the buffers are missing `GPUBufferUsage.COPY_DST` or `GPUBufferUsage.COPY_SRC`.
|
|
3
3
|
* This is a pure utility shader.
|
|
4
|
+
*
|
|
5
|
+
* Copies the first `size` words. The launch may be smaller than that — a copy longer than
|
|
6
|
+
* `maxComputeWorkgroupsPerDimension` workgroups has to be — and every lane strides through the rest
|
|
7
|
+
* by the whole grid, so a caller that clamps the launch still has every word copied.
|
|
8
|
+
*
|
|
4
9
|
* @type {ComputeShader}
|
|
5
10
|
*/
|
|
6
11
|
export const shader_buffer_copy_storage_to_storage: ComputeShader;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_buffer_copy_storage_to_storage.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js"],"names":[],"mappings":"AAkCA
|
|
1
|
+
{"version":3,"file":"shader_buffer_copy_storage_to_storage.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js"],"names":[],"mappings":"AAkCA;;;;;;;;;GASG;AACH,oDAFU,aAAa,CAMpB;8BA5C2B,+BAA+B"}
|