@woosh/meep-engine 3.31.0 → 3.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem.js +6 -2
- package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -1
- package/src/engine/graphics3/PathDisplaySystem.js +311 -307
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/RENDERER_CONTRACT.md +15 -2
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +5 -0
- package/src/shade/device/ShadeGPUCommandContext.d.ts +15 -2
- package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
- package/src/shade/device/ShadeGPUCommandContext.js +154 -25
- package/src/shade/renderer/GraphicsContext.d.ts +9 -1
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +54 -15
- package/src/shade/renderer/Renderer.d.ts +19 -4
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +113 -2
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -7
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +147 -45
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts +11 -2
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.js +95 -13
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts +60 -0
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts.map +1 -0
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.js +202 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.d.ts +68 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.d.ts.map +1 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.js +97 -0
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts +20 -7
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +20 -7
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +5 -0
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +19 -0
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +3 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +3 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +134 -88
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +57 -26
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +28 -20
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts +38 -0
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.js +105 -0
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +21 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +12 -20
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts +2 -1
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts.map +1 -1
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.js +15 -28
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +0 -73
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts +0 -59
- package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts +0 -22
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts +0 -26
- package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts +0 -25
- package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts +0 -26
- package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts +0 -29
- package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts.map +0 -1
|
@@ -85,6 +85,7 @@ import { View } from "./view/View.js";
|
|
|
85
85
|
import { ViewManager } from "./view/ViewManager.js";
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
+
* @typedef {import("../device/timing/profile/GPUFrameRecorder.js").GPUFrameRecorder} GPUFrameRecorder
|
|
88
89
|
* @typedef {import("../device/timing/profile/GPUProfileSession.js").GPUProfileSession} GPUProfileSession
|
|
89
90
|
* @typedef {import("./camera/PerspectiveCamera.js").PerspectiveCamera} PerspectiveCamera
|
|
90
91
|
* @typedef {import("./extension/RenderExtension.js").RenderExtension} RenderExtension
|
|
@@ -472,6 +473,17 @@ export class Renderer {
|
|
|
472
473
|
*/
|
|
473
474
|
onDeviceFailure = new Signal();
|
|
474
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Sent at the end of every frame {@link render} or {@link render_to_target} completes, with the
|
|
478
|
+
* number of frames completed so far. A frame that throws is not one of them: whatever it had
|
|
479
|
+
* recorded may still have been submitted, but it sends nothing and is not counted.
|
|
480
|
+
*
|
|
481
|
+
* **Submitted, not finished.** The frame's commands have been handed to the queue and the GPU
|
|
482
|
+
* may not have started them; a listener that needs the GPU to be done with the frame waits on
|
|
483
|
+
* {@link GPUQueue#onSubmittedWorkDone} instead.
|
|
484
|
+
*
|
|
485
|
+
* @type {Signal<number>}
|
|
486
|
+
*/
|
|
475
487
|
onFrameFinished = new Signal();
|
|
476
488
|
|
|
477
489
|
/**
|
|
@@ -1898,7 +1910,9 @@ export class Renderer {
|
|
|
1898
1910
|
* simulation, the animation tick and exposure adaptation all step by it. Zero by default, which
|
|
1899
1911
|
* draws the current state and moves nothing; a fixed fraction of a second here instead would run
|
|
1900
1912
|
* the simulation at that rate no matter what the caller's clock was doing.
|
|
1901
|
-
* @returns {boolean} whether frame was
|
|
1913
|
+
* @returns {boolean} whether a frame was encoded and submitted. False only when there is nothing
|
|
1914
|
+
* to draw with — the device was lost, or the renderer destroyed. Frames the GPU has not finished
|
|
1915
|
+
* yet neither hold this one back nor make it return false.
|
|
1902
1916
|
*/
|
|
1903
1917
|
render(
|
|
1904
1918
|
camera,
|
|
@@ -1937,13 +1951,100 @@ export class Renderer {
|
|
|
1937
1951
|
* @param {Scene} scene
|
|
1938
1952
|
* @param {number} time_delta_seconds
|
|
1939
1953
|
* @param {GPUTextureContext} target must match {@link output_resolution}
|
|
1940
|
-
* @returns {boolean} whether frame was
|
|
1954
|
+
* @returns {boolean} whether a frame was encoded and submitted. False only when there is nothing
|
|
1955
|
+
* to draw with — the device was lost, or the renderer destroyed. Frames the GPU has not finished
|
|
1956
|
+
* yet neither hold this one back nor make it return false.
|
|
1941
1957
|
*/
|
|
1942
1958
|
render_to_target(
|
|
1943
1959
|
camera,
|
|
1944
1960
|
scene,
|
|
1945
1961
|
time_delta_seconds,
|
|
1946
1962
|
target
|
|
1963
|
+
) {
|
|
1964
|
+
try {
|
|
1965
|
+
return this.#draw_to_target(camera, scene, time_delta_seconds, target);
|
|
1966
|
+
} catch (error) {
|
|
1967
|
+
this.#finish_stranded_frame();
|
|
1968
|
+
|
|
1969
|
+
throw error;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* The frame's command context from the moment it is created until the frame's teardown takes
|
|
1975
|
+
* it, and null otherwise — so a frame that throws before its teardown leaves it here for
|
|
1976
|
+
* {@link render_to_target} to finish.
|
|
1977
|
+
*
|
|
1978
|
+
* @type {ShadeGPUCommandContext|null}
|
|
1979
|
+
*/
|
|
1980
|
+
#frame_command_ctx = null;
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* The profiler's recorder for the frame {@link #frame_command_ctx} belongs to, and the session
|
|
1984
|
+
* that handed it out — null when that frame is not profiled — so a frame that throws before its
|
|
1985
|
+
* teardown still closes its profile frame. Otherwise a capture leaves out exactly the frames
|
|
1986
|
+
* that failed.
|
|
1987
|
+
*
|
|
1988
|
+
* @type {GPUFrameRecorder|null}
|
|
1989
|
+
*/
|
|
1990
|
+
#frame_profile_recorder = null;
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* @type {GPUProfileSession|null}
|
|
1994
|
+
*/
|
|
1995
|
+
#frame_profile_session = null;
|
|
1996
|
+
|
|
1997
|
+
/**
|
|
1998
|
+
* Finish the context a frame left behind when it threw before its teardown: the scene tick, the
|
|
1999
|
+
* shadow draw and every extension phase run between the context's creation and the teardown,
|
|
2000
|
+
* and a throw from any of them used to leave it open for good, holding its upload block, its
|
|
2001
|
+
* transient buffers and its profiler's buffers — every frame it happened.
|
|
2002
|
+
*
|
|
2003
|
+
* What comes back is what it holds. What it recorded is submitted if the encoder can still be
|
|
2004
|
+
* finished: a throw once the `Shade` debug group is open leaves that group unclosed, the
|
|
2005
|
+
* command buffer is invalid, and the submission is refused with nothing of the frame run — as it
|
|
2006
|
+
* is for a pass that throws, whose teardown shares the same unclosed group.
|
|
2007
|
+
*/
|
|
2008
|
+
#finish_stranded_frame() {
|
|
2009
|
+
const stranded = this.#frame_command_ctx;
|
|
2010
|
+
const recorder = this.#frame_profile_recorder;
|
|
2011
|
+
const session = this.#frame_profile_session;
|
|
2012
|
+
|
|
2013
|
+
this.#frame_command_ctx = null;
|
|
2014
|
+
this.#frame_profile_recorder = null;
|
|
2015
|
+
this.#frame_profile_session = null;
|
|
2016
|
+
|
|
2017
|
+
if (stranded === null) {
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
try {
|
|
2022
|
+
stranded.finish();
|
|
2023
|
+
} catch (teardown_error) {
|
|
2024
|
+
// already unwinding: the frame's own error is the one the caller needs to see
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
if (recorder !== null) {
|
|
2028
|
+
stranded.profiling_absorbed.then(() => {
|
|
2029
|
+
session.end_frame(recorder, performance.now());
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* The whole of {@link render_to_target}, which only adds the finishing of a frame that throws.
|
|
2036
|
+
*
|
|
2037
|
+
* @param {PerspectiveCamera} camera
|
|
2038
|
+
* @param {Scene} scene
|
|
2039
|
+
* @param {number} time_delta_seconds
|
|
2040
|
+
* @param {GPUTextureContext} target
|
|
2041
|
+
* @returns {boolean}
|
|
2042
|
+
*/
|
|
2043
|
+
#draw_to_target(
|
|
2044
|
+
camera,
|
|
2045
|
+
scene,
|
|
2046
|
+
time_delta_seconds,
|
|
2047
|
+
target
|
|
1947
2048
|
) {
|
|
1948
2049
|
assert.defined(target, 'target');
|
|
1949
2050
|
assert.equal(target.isGPUTextureContext, true, 'target.isGPUTextureContext !== true');
|
|
@@ -2020,6 +2121,8 @@ export class Renderer {
|
|
|
2020
2121
|
"Renderer/main-0"
|
|
2021
2122
|
);
|
|
2022
2123
|
|
|
2124
|
+
this.#frame_command_ctx = command_ctx;
|
|
2125
|
+
|
|
2023
2126
|
/**
|
|
2024
2127
|
* The frame recorder for this frame, or null when nothing is profiling.
|
|
2025
2128
|
* @type {GPUFrameRecorder|null}
|
|
@@ -2033,6 +2136,9 @@ export class Renderer {
|
|
|
2033
2136
|
|
|
2034
2137
|
if (profile_recorder !== null) {
|
|
2035
2138
|
command_ctx.enable_profiling(profile_recorder, 0);
|
|
2139
|
+
|
|
2140
|
+
this.#frame_profile_recorder = profile_recorder;
|
|
2141
|
+
this.#frame_profile_session = this.profile_session;
|
|
2036
2142
|
}
|
|
2037
2143
|
}
|
|
2038
2144
|
|
|
@@ -2822,6 +2928,11 @@ export class Renderer {
|
|
|
2822
2928
|
destroyed, and the session's frame accounting silently short a frame. A render loop that
|
|
2823
2929
|
catches and carries on repeats that every failing frame until the device complains.
|
|
2824
2930
|
*/
|
|
2931
|
+
// the teardown has it from here, whatever the finish does
|
|
2932
|
+
this.#frame_command_ctx = null;
|
|
2933
|
+
this.#frame_profile_recorder = null;
|
|
2934
|
+
this.#frame_profile_session = null;
|
|
2935
|
+
|
|
2825
2936
|
try {
|
|
2826
2937
|
command_ctx.finish();
|
|
2827
2938
|
} catch (teardown_error) {
|
|
@@ -418,8 +418,10 @@ export class GPUAnimationManager {
|
|
|
418
418
|
* correct ping-pong behaviour across frames without leaking the
|
|
419
419
|
* buffer-role bookkeeping into every caller.
|
|
420
420
|
*
|
|
421
|
-
* No-op when the joints table has no
|
|
422
|
-
* (`group_count_x === 0`)
|
|
421
|
+
* No-op when the joints table has no page for it to walk
|
|
422
|
+
* (`group_count_x === 0`), which is never while a skin is
|
|
423
|
+
* registered: a page emptied of joints goes at the next database
|
|
424
|
+
* update.
|
|
423
425
|
*
|
|
424
426
|
* Per-frame ordering:
|
|
425
427
|
* 1. Flip {@link #skin_matrices_current_index}: last frame's
|
|
@@ -427,11 +429,12 @@ export class GPUAnimationManager {
|
|
|
427
429
|
* ({@link prev_skin_matrices_buffer}), and the buffer that
|
|
428
430
|
* held the frame before it becomes the write target.
|
|
429
431
|
* 2. Dispatch the prep shader into the new "current".
|
|
430
|
-
* 3.
|
|
431
|
-
* out
|
|
432
|
-
* since the last dispatch
|
|
433
|
-
*
|
|
434
|
-
*
|
|
432
|
+
* 3. Copy "current" over "previous" on the rows that have no last
|
|
433
|
+
* frame — every run handed out and every pending skin's run
|
|
434
|
+
* named since the last dispatch, or all of it after a grow —
|
|
435
|
+
* so the first velocity read over those rows sees matching
|
|
436
|
+
* matrices and produces zero velocity. Every other row of
|
|
437
|
+
* "previous" keeps last frame's matrices.
|
|
435
438
|
*
|
|
436
439
|
* @param {ShadeGPUCommandContext} cmd_ctx
|
|
437
440
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GPUAnimationManager.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/GPUAnimationManager.js"],"names":[],"mappings":"AA+IA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH;
|
|
1
|
+
{"version":3,"file":"GPUAnimationManager.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/animation/GPUAnimationManager.js"],"names":[],"mappings":"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"}
|
|
@@ -200,8 +200,8 @@ export class GPUAnimationManager {
|
|
|
200
200
|
*
|
|
201
201
|
* Lazily allocated on first {@link add_skin}; both grow together
|
|
202
202
|
* by doubling. After a grow both buffers are zero-initialised and
|
|
203
|
-
* {@link #
|
|
204
|
-
*
|
|
203
|
+
* {@link #skin_matrix_seed_all} is set so the next prep dispatch
|
|
204
|
+
* seeds all of the "previous" buffer with a copy of the freshly
|
|
205
205
|
* written "current" — without this, the first velocity readback
|
|
206
206
|
* after a grow would interpolate against zero matrices.
|
|
207
207
|
*
|
|
@@ -217,15 +217,31 @@ export class GPUAnimationManager {
|
|
|
217
217
|
#skin_matrices_current_index = 0;
|
|
218
218
|
|
|
219
219
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
220
|
+
* The runs of matrix rows the next prep dispatch seeds "previous" from "current" over, each
|
|
221
|
+
* `[first_row, count]`: a run handed out, and a pending skin's run whose joints were just
|
|
222
|
+
* named. The frame before either is not a pose — "previous" over those rows holds zeros, or
|
|
223
|
+
* the last pose of a skin that had them — so the first velocity read over them has to see
|
|
224
|
+
* matching matrices, which is zero velocity.
|
|
225
|
+
*
|
|
226
|
+
* Only those rows: a settled skin's rows of "previous" hold its pose last frame, which is what
|
|
227
|
+
* its motion is measured from.
|
|
228
|
+
*
|
|
229
|
+
* A skin given back takes its queued runs with it ({@link unregister_skin}), so every run here
|
|
230
|
+
* belongs to a skin still registered and not yet seeded — however long no prep runs, as while
|
|
231
|
+
* a loading screen draws nothing and the game goes on spawning.
|
|
232
|
+
*
|
|
233
|
+
* @type {Array<[number, number]>}
|
|
234
|
+
*/
|
|
235
|
+
#skin_matrix_seed_runs = [];
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* When true, the next prep dispatch seeds all of "previous" from "current", whatever
|
|
239
|
+
* {@link #skin_matrix_seed_runs} holds. Set when the buffers grow: both are made again,
|
|
240
|
+
* zeroed, so no row has a last frame left to keep.
|
|
241
|
+
*
|
|
226
242
|
* @type {boolean}
|
|
227
243
|
*/
|
|
228
|
-
#
|
|
244
|
+
#skin_matrix_seed_all = false;
|
|
229
245
|
|
|
230
246
|
/**
|
|
231
247
|
* Capacity of each of {@link #skin_matrices_buffers} in matrices
|
|
@@ -1056,7 +1072,7 @@ export class GPUAnimationManager {
|
|
|
1056
1072
|
// The joints' matrices are written from the next prep on, and "previous" over their rows
|
|
1057
1073
|
// holds whatever was there before — zeros, or the last pose of a skin that had them — so
|
|
1058
1074
|
// it is seeded, as it is for a run just handed out.
|
|
1059
|
-
this.#
|
|
1075
|
+
this.#skin_matrix_seed_runs.push([pending.joints_first, pending.joint_count]);
|
|
1060
1076
|
|
|
1061
1077
|
return true;
|
|
1062
1078
|
}
|
|
@@ -1436,10 +1452,34 @@ export class GPUAnimationManager {
|
|
|
1436
1452
|
return;
|
|
1437
1453
|
}
|
|
1438
1454
|
|
|
1455
|
+
// Its rows go back owed no seed. Left queued, a run outlives its skin until the next prep,
|
|
1456
|
+
// and while nothing draws that is every spawn since drawing stopped.
|
|
1457
|
+
this.#drop_seed_runs(skins_table.get_word(skin_id, SKIN_JOINTS_FIRST_WORD));
|
|
1458
|
+
|
|
1439
1459
|
// The joints go with the skin — a pending skin's too, nodes or not.
|
|
1440
1460
|
skins_table.remove(skin_id);
|
|
1441
1461
|
}
|
|
1442
1462
|
|
|
1463
|
+
/**
|
|
1464
|
+
* Drop every queued seed run starting at `first_row`: the run of a skin being given back,
|
|
1465
|
+
* queued when it was handed out, and again if its joints have been named since.
|
|
1466
|
+
*
|
|
1467
|
+
* No other skin's run can start there, since a skin's rows are its own until it is given back.
|
|
1468
|
+
*
|
|
1469
|
+
* @param {number} first_row
|
|
1470
|
+
*/
|
|
1471
|
+
#drop_seed_runs(first_row) {
|
|
1472
|
+
const runs = this.#skin_matrix_seed_runs;
|
|
1473
|
+
|
|
1474
|
+
// the prep sorts what it finds, so a dropped run's place goes to the last one
|
|
1475
|
+
for (let i = runs.length - 1; i >= 0; i--) {
|
|
1476
|
+
if (runs[i][0] === first_row) {
|
|
1477
|
+
runs[i] = runs[runs.length - 1];
|
|
1478
|
+
runs.pop();
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1443
1483
|
/**
|
|
1444
1484
|
* Cached wrapper around {@link add_curve}: one upload per distinct curve, where distinct means
|
|
1445
1485
|
* distinct *content*.
|
|
@@ -1541,25 +1581,31 @@ export class GPUAnimationManager {
|
|
|
1541
1581
|
// its rows are fresh or some despawned skin's, so the next prep dispatch has to seed
|
|
1542
1582
|
// "previous" from "current" for it.
|
|
1543
1583
|
//
|
|
1544
|
-
//
|
|
1545
|
-
//
|
|
1546
|
-
//
|
|
1547
|
-
// `
|
|
1548
|
-
//
|
|
1549
|
-
//
|
|
1550
|
-
//
|
|
1551
|
-
|
|
1552
|
-
this.#first_frame_skin_matrices = true;
|
|
1584
|
+
// Whether or not the buffers grew: rows given back by another skin never grow them, and
|
|
1585
|
+
// neither do fresh rows below the capacity, so "previous" over the run holds all-zero
|
|
1586
|
+
// matrices or the last pose of the skin that had those rows before. The skinning pass
|
|
1587
|
+
// reads `prev_skin_matrices[skin_base + j]` unconditionally, so unseeded, the new
|
|
1588
|
+
// character's first frame would take its motion vectors from someone else's pose — a
|
|
1589
|
+
// screen-crossing smear through TAA and motion blur — on exactly the spawn/despawn loop
|
|
1590
|
+
// that reuses rows.
|
|
1591
|
+
this.#skin_matrix_seed_runs.push([joints_first, joints.length]);
|
|
1553
1592
|
|
|
1554
1593
|
return joints_first;
|
|
1555
1594
|
}
|
|
1556
1595
|
|
|
1557
1596
|
/**
|
|
1558
|
-
* Grow the skinning-matrix
|
|
1559
|
-
*
|
|
1560
|
-
*
|
|
1561
|
-
*
|
|
1562
|
-
*
|
|
1597
|
+
* Grow the skinning-matrix buffers if `required_count` exceeds
|
|
1598
|
+
* current capacity. Doubles on grow until the requirement fits.
|
|
1599
|
+
*
|
|
1600
|
+
* Nothing is copied across. "Current" would be rewritten by the
|
|
1601
|
+
* next prep anyway, but "previous" held every skin's pose last
|
|
1602
|
+
* frame, which its motion is measured from: the grow loses that,
|
|
1603
|
+
* and the next prep seeds all of "previous" from "current" instead
|
|
1604
|
+
* ({@link #skin_matrix_seed_all}), so every skinned character has
|
|
1605
|
+
* zero motion vectors for that one frame. Capacity doubles, so
|
|
1606
|
+
* that is a handful of frames over a session.
|
|
1607
|
+
*
|
|
1608
|
+
* The grow also invalidates the {@link #skin_matrices_buffer}
|
|
1563
1609
|
* reference, which is why callers re-fetch through the getter.
|
|
1564
1610
|
*
|
|
1565
1611
|
* @param {number} required_count number of matrices that must fit
|
|
@@ -1578,8 +1624,9 @@ export class GPUAnimationManager {
|
|
|
1578
1624
|
// capacity. After grow both are zero-initialised, so the next
|
|
1579
1625
|
// prep dispatch needs to seed "previous" from "current"
|
|
1580
1626
|
// (otherwise velocity would interpolate against zero
|
|
1581
|
-
// matrices)
|
|
1582
|
-
//
|
|
1627
|
+
// matrices), and all of it: no row has a last frame left to
|
|
1628
|
+
// keep. The flag below tells dispatch_skin_matrix_prep to
|
|
1629
|
+
// issue that copy.
|
|
1583
1630
|
for (let i = 0; i < 2; i++) {
|
|
1584
1631
|
if (this.#skin_matrices_buffers[i] !== null) {
|
|
1585
1632
|
this.#skin_matrices_buffers[i].destroy();
|
|
@@ -1591,7 +1638,7 @@ export class GPUAnimationManager {
|
|
|
1591
1638
|
});
|
|
1592
1639
|
}
|
|
1593
1640
|
this.#skin_matrices_capacity = new_capacity;
|
|
1594
|
-
this.#
|
|
1641
|
+
this.#skin_matrix_seed_all = true;
|
|
1595
1642
|
}
|
|
1596
1643
|
|
|
1597
1644
|
/**
|
|
@@ -1643,8 +1690,10 @@ export class GPUAnimationManager {
|
|
|
1643
1690
|
* correct ping-pong behaviour across frames without leaking the
|
|
1644
1691
|
* buffer-role bookkeeping into every caller.
|
|
1645
1692
|
*
|
|
1646
|
-
* No-op when the joints table has no
|
|
1647
|
-
* (`group_count_x === 0`)
|
|
1693
|
+
* No-op when the joints table has no page for it to walk
|
|
1694
|
+
* (`group_count_x === 0`), which is never while a skin is
|
|
1695
|
+
* registered: a page emptied of joints goes at the next database
|
|
1696
|
+
* update.
|
|
1648
1697
|
*
|
|
1649
1698
|
* Per-frame ordering:
|
|
1650
1699
|
* 1. Flip {@link #skin_matrices_current_index}: last frame's
|
|
@@ -1652,11 +1701,12 @@ export class GPUAnimationManager {
|
|
|
1652
1701
|
* ({@link prev_skin_matrices_buffer}), and the buffer that
|
|
1653
1702
|
* held the frame before it becomes the write target.
|
|
1654
1703
|
* 2. Dispatch the prep shader into the new "current".
|
|
1655
|
-
* 3.
|
|
1656
|
-
* out
|
|
1657
|
-
* since the last dispatch
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1704
|
+
* 3. Copy "current" over "previous" on the rows that have no last
|
|
1705
|
+
* frame — every run handed out and every pending skin's run
|
|
1706
|
+
* named since the last dispatch, or all of it after a grow —
|
|
1707
|
+
* so the first velocity read over those rows sees matching
|
|
1708
|
+
* matrices and produces zero velocity. Every other row of
|
|
1709
|
+
* "previous" keeps last frame's matrices.
|
|
1660
1710
|
*
|
|
1661
1711
|
* @param {ShadeGPUCommandContext} cmd_ctx
|
|
1662
1712
|
*/
|
|
@@ -1665,6 +1715,9 @@ export class GPUAnimationManager {
|
|
|
1665
1715
|
.get('animation_skin_joints')
|
|
1666
1716
|
.dispatch_group_count(SKIN_MATRIX_PREP_WORKGROUP_SIZE);
|
|
1667
1717
|
if (groups === 0) {
|
|
1718
|
+
// no page to walk means no skin registered, and a skin given back takes its runs
|
|
1719
|
+
assert.equal(this.#skin_matrix_seed_runs.length, 0, 'seed runs queued for skins no longer registered');
|
|
1720
|
+
|
|
1668
1721
|
return;
|
|
1669
1722
|
}
|
|
1670
1723
|
|
|
@@ -1689,20 +1742,69 @@ export class GPUAnimationManager {
|
|
|
1689
1742
|
},
|
|
1690
1743
|
});
|
|
1691
1744
|
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1745
|
+
this.#seed_previous_skin_matrices(cmd_ctx, current, previous);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* Copy "current" over "previous" on the rows where "previous" holds no pose of the frame
|
|
1750
|
+
* before: all of it after a grow, otherwise the runs queued since the last prep. Zeros there,
|
|
1751
|
+
* or the last pose of a skin that had the rows, would have the velocity pass produce huge
|
|
1752
|
+
* nonsense on the first frame the skin appears; seeded, the first velocity read yields zero,
|
|
1753
|
+
* which is the right answer — no motion has happened yet.
|
|
1754
|
+
*
|
|
1755
|
+
* Every other row is left alone: a settled skin's rows hold its pose last frame, which its
|
|
1756
|
+
* motion is measured from, and the rest are read by no one. Seeded too, every skinned
|
|
1757
|
+
* character on screen would lose its motion vectors for the frame whenever one spawned.
|
|
1758
|
+
*
|
|
1759
|
+
* Runs are coalesced by sorting — a frame's worth of spawns is a handful of runs — and adjacent
|
|
1760
|
+
* or overlapping ones go as one copy.
|
|
1761
|
+
*
|
|
1762
|
+
* @param {ShadeGPUCommandContext} cmd_ctx
|
|
1763
|
+
* @param {GPUBuffer} current
|
|
1764
|
+
* @param {GPUBuffer} previous
|
|
1765
|
+
*/
|
|
1766
|
+
#seed_previous_skin_matrices(cmd_ctx, current, previous) {
|
|
1767
|
+
const runs = this.#skin_matrix_seed_runs;
|
|
1768
|
+
|
|
1769
|
+
if (this.#skin_matrix_seed_all) {
|
|
1770
|
+
cmd_ctx.copyBufferToBuffer(current, 0, previous, 0, current.size);
|
|
1771
|
+
|
|
1772
|
+
this.#skin_matrix_seed_all = false;
|
|
1773
|
+
runs.length = 0;
|
|
1774
|
+
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
if (runs.length === 0) {
|
|
1779
|
+
// a settled frame: every row of "previous" is last frame's
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
runs.sort((a, b) => a[0] - b[0]);
|
|
1784
|
+
|
|
1785
|
+
let i = 0;
|
|
1786
|
+
|
|
1787
|
+
while (i < runs.length) {
|
|
1788
|
+
const first = runs[i][0];
|
|
1789
|
+
let end = first + runs[i][1];
|
|
1790
|
+
|
|
1791
|
+
i++;
|
|
1792
|
+
|
|
1793
|
+
// a run starting at or before this copy's end is part of it
|
|
1794
|
+
while (i < runs.length && runs[i][0] <= end) {
|
|
1795
|
+
end = Math.max(end, runs[i][0] + runs[i][1]);
|
|
1796
|
+
|
|
1797
|
+
i++;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1699
1800
|
cmd_ctx.copyBufferToBuffer(
|
|
1700
|
-
current,
|
|
1701
|
-
previous,
|
|
1702
|
-
|
|
1801
|
+
current, first * SKIN_MATRIX_BYTES,
|
|
1802
|
+
previous, first * SKIN_MATRIX_BYTES,
|
|
1803
|
+
(end - first) * SKIN_MATRIX_BYTES,
|
|
1703
1804
|
);
|
|
1704
|
-
this.#first_frame_skin_matrices = false;
|
|
1705
1805
|
}
|
|
1806
|
+
|
|
1807
|
+
runs.length = 0;
|
|
1706
1808
|
}
|
|
1707
1809
|
|
|
1708
1810
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Mapped `MAP_WRITE | COPY_SRC` buffers for pushing data from the CPU to the GPU, one per upload.
|
|
3
3
|
*
|
|
4
|
+
* A buffer comes out mapped; its holder fills it, unmaps it, copies out of it and gives it back, and
|
|
5
|
+
* the pool maps it again — a round trip through the GPU — before it can come out again. For bytes
|
|
6
|
+
* that is the cheaper road; for thousands of small writes a frame the round trips are the cost, which
|
|
7
|
+
* is why a {@link ShadeGPUCommandContext} brings only its large writes here (see
|
|
8
|
+
* `MAPPED_UPLOAD_THRESHOLD`).
|
|
4
9
|
*/
|
|
5
10
|
export class GPUBufferStagingAllocator {
|
|
6
11
|
/**
|
|
@@ -9,6 +14,8 @@ export class GPUBufferStagingAllocator {
|
|
|
9
14
|
*/
|
|
10
15
|
constructor(device: GPUDevice);
|
|
11
16
|
/**
|
|
17
|
+
* Bytes in every buffer this pool has made and not destroyed: free, handed out, or on the way
|
|
18
|
+
* back.
|
|
12
19
|
*
|
|
13
20
|
* @returns {number}
|
|
14
21
|
*/
|
|
@@ -22,8 +29,10 @@ export class GPUBufferStagingAllocator {
|
|
|
22
29
|
/**
|
|
23
30
|
*
|
|
24
31
|
* @param {GPUBuffer} buffer
|
|
32
|
+
* @returns {boolean} whether it was taken back: false for a buffer given back already, or one
|
|
33
|
+
* this pool did not hand out
|
|
25
34
|
*/
|
|
26
|
-
release(buffer: GPUBuffer):
|
|
35
|
+
release(buffer: GPUBuffer): boolean;
|
|
27
36
|
destroy(): void;
|
|
28
37
|
#private;
|
|
29
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GPUBufferStagingAllocator.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/GPUBufferStagingAllocator.js"],"names":[],"mappings":"AASA
|
|
1
|
+
{"version":3,"file":"GPUBufferStagingAllocator.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/buffer/GPUBufferStagingAllocator.js"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH;IA8CI;;;OAGG;IACH,+BAIC;IAED;;;;;OAKG;IACH,+BAEC;IAGD;;;;OAIG;IACH,oBAHW,MAAM,aAsChB;IAkCD;;;;;OAKG;IACH,4BAHa,OAAO,CA0BnB;IAgCD,gBAUC;;CACJ"}
|