@woosh/meep-engine 2.168.0 → 2.169.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/bundle-worker-image-decoder.js +1 -1
- package/package.json +1 -1
- package/src/REVIEW_2026_08_06/adv-core.md +511 -0
- package/src/REVIEW_2026_08_06/adv-gfx.md +480 -0
- package/src/REVIEW_2026_08_06/adv-view.md +446 -0
- package/src/REVIEW_2026_08_06/api-core.md +296 -0
- package/src/REVIEW_2026_08_06/api-engine.md +195 -0
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -0
- package/src/REVIEW_2026_08_06/core-binary.md +348 -0
- package/src/REVIEW_2026_08_06/core-collection.md +376 -0
- package/src/REVIEW_2026_08_06/core-math.md +221 -0
- package/src/REVIEW_2026_08_06/core-process.md +266 -0
- package/src/REVIEW_2026_08_06/core-science.md +223 -0
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -0
- package/src/REVIEW_2026_08_06/engine-animation.md +167 -0
- package/src/REVIEW_2026_08_06/engine-asset.md +166 -0
- package/src/REVIEW_2026_08_06/engine-ecs.md +920 -0
- package/src/REVIEW_2026_08_06/engine-input.md +197 -0
- package/src/REVIEW_2026_08_06/engine-misc.md +248 -0
- package/src/REVIEW_2026_08_06/engine-terrain.md +52 -0
- package/src/REVIEW_2026_08_06/generation.md +176 -0
- package/src/REVIEW_2026_08_06/geom-2d.md +434 -0
- package/src/REVIEW_2026_08_06/geom-3d.md +117 -0
- package/src/REVIEW_2026_08_06/gfx-gi.md +143 -0
- package/src/REVIEW_2026_08_06/gfx-render.md +740 -0
- package/src/REVIEW_2026_08_06/gfx-texture.md +690 -0
- package/src/REVIEW_2026_08_06/prior-criticals.md +49 -0
- package/src/REVIEW_2026_08_06/prior-highs.md +138 -0
- package/src/REVIEW_2026_08_06/test-gaps.md +364 -0
- package/src/REVIEW_2026_08_06/view.md +667 -0
- package/src/REVIEW_2026_08_06.md +571 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +12 -1
- package/src/core/binary/BitImage2.d.ts +5 -1
- package/src/core/binary/BitImage2.d.ts.map +1 -1
- package/src/core/binary/BitImage2.js +5 -1
- package/src/core/bvh8/build/NodeProxy.d.ts.map +1 -1
- package/src/core/bvh8/build/NodeProxy.js +314 -308
- package/src/core/collection/list/List.d.ts +15 -0
- package/src/core/collection/list/List.d.ts.map +1 -1
- package/src/core/collection/list/List.js +34 -0
- package/src/core/collection/list/SortedListProjection.d.ts.map +1 -1
- package/src/core/collection/list/SortedListProjection.js +4 -1
- package/src/core/collection/map/BiMap.d.ts +25 -2
- package/src/core/collection/map/BiMap.d.ts.map +1 -1
- package/src/core/collection/map/BiMap.js +57 -3
- package/src/core/debug/matchers/IsAnything.d.ts.map +1 -1
- package/src/core/debug/matchers/IsAnything.js +5 -1
- package/src/core/events/signal/Signal.d.ts.map +1 -1
- package/src/core/events/signal/Signal.js +3 -1
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts +31 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -0
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts +24 -2
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -34
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts +40 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -0
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +6 -3
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +26 -13
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts +17 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -23
- package/src/core/geom/2d/v2_morton_encode.d.ts +20 -0
- package/src/core/geom/2d/v2_morton_encode.d.ts.map +1 -0
- package/src/core/geom/2d/v2_morton_encode.js +23 -0
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +4 -1
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +27 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts +34 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +8 -2
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.js +8 -2
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +32 -2
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts +23 -6
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -124
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts +17 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.js +40 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.js +34 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.js +37 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts +87 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -0
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts +32 -0
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.js +66 -7
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts +22 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.js +83 -38
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -0
- package/src/core/geom/vec3/v3_cotangent.d.ts +6 -2
- package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cotangent.js +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cross.js +6 -2
- package/src/core/geom/vec3/v3_multiply.d.ts +6 -2
- package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_multiply.js +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_subtract.js +6 -2
- package/src/core/localization/Localization.js +1 -1
- package/src/core/math/random/randomSeed.d.ts +13 -0
- package/src/core/math/random/randomSeed.d.ts.map +1 -0
- package/src/core/math/random/randomSeed.js +16 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts +50 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts +34 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_reverse.js +49 -0
- package/src/core/math/statistics/hammersley_sequence.d.ts +17 -4
- package/src/core/math/statistics/hammersley_sequence.d.ts.map +1 -1
- package/src/core/math/statistics/hammersley_sequence.js +25 -16
- package/src/core/process/Future.d.ts.map +1 -1
- package/src/core/process/Future.js +12 -0
- package/src/core/process/PromiseWatcher.d.ts.map +1 -1
- package/src/core/process/PromiseWatcher.js +9 -2
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +12 -2
- package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
- package/src/core/process/worker/WorkerProxy.js +5 -2
- package/src/core/process/worker/extractTransferables.d.ts.map +1 -1
- package/src/core/process/worker/extractTransferables.js +4 -1
- package/src/engine/Engine.d.ts +12 -12
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +149 -10
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +4 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.js +7 -0
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.d.ts.map +1 -1
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.js +3 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.js +4 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts +29 -0
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +84 -23
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -0
- package/src/engine/graphics/GraphicsEngine.d.ts +56 -7
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +196 -24
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts +9 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.js +17 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts +106 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextMonitor.js +341 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts +14 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextState.js +24 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts +2 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts.map +1 -0
- package/src/engine/graphics/context/testWebGLContextLoss.js +696 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -32
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts +14 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.js +41 -0
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +5 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +5 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.js +15 -0
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +2 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +4 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +488 -476
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +280 -253
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +3 -2
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +3 -1
- package/src/engine/graphics/geometry/computeMeshSurfaceArea.js +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts +5 -0
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts.map +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.js +26 -21
- package/src/engine/graphics/particles/particular/group/ParticleGroup.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/group/ParticleGroup.js +19 -8
- package/src/engine/graphics/particles/particular/group/optimizeCommandQueue.js +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.js +10 -3
- package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/TextureAtlas.js +4 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js +11 -0
- package/src/engine/graphics/texture/cubemap/load_environment_map.d.ts.map +1 -1
- package/src/engine/graphics/texture/cubemap/load_environment_map.js +5 -2
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.js +26 -18
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts +15 -2
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.js +29 -3
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts +11 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +158 -171
- package/src/engine/graphics/texture/sampler/sampler2d_paint.js +1 -1
- package/src/engine/graphics/texture/sampler/util/bitSet2Sampler2D.js +2 -2
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.d.ts.map +1 -1
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.js +10 -3
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts +14 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts.map +1 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.js +38 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +433 -425
- package/src/engine/physics/constraint/solve_constraints.d.ts +4 -1
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +38 -13
- package/src/engine/physics/contact/ManifoldStore.d.ts +15 -3
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +15 -3
- package/src/engine/physics/ecs/Joint.d.ts +7 -4
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
- package/src/engine/physics/ecs/Joint.js +7 -4
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +14 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +16 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +15 -7
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +33 -10
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +6 -2
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +11 -4
- package/src/engine/save/storage/IndexedDBStorage.d.ts.map +1 -1
- package/src/engine/save/storage/IndexedDBStorage.js +21 -3
- package/src/engine/simulation/Ticker.d.ts +12 -0
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +18 -0
- package/src/engine/sound/simulation/AcousticSimulator.d.ts.map +1 -1
- package/src/engine/sound/simulation/AcousticSimulator.js +9 -1
- package/src/engine/sound/simulation/core/VolumeField.d.ts +6 -2
- package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/VolumeField.js +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +6 -2
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts +3 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +3 -1
- package/src/engine/ui/GUIEngine.d.ts.map +1 -1
- package/src/engine/ui/GUIEngine.js +8 -1
- package/src/generation/theme/TerrainTheme.d.ts.map +1 -1
- package/src/generation/theme/TerrainTheme.js +2 -1
- package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
- package/src/generation/theme/ThemeEngine.js +6 -5
- package/src/view/ViewGroup.d.ts.map +1 -1
- package/src/view/ViewGroup.js +9 -7
- package/src/view/graphics/WebGLContextFailureView.d.ts +19 -0
- package/src/view/graphics/WebGLContextFailureView.d.ts.map +1 -0
- package/src/view/graphics/WebGLContextFailureView.js +76 -0
- package/src/view/minimap/Minimap.d.ts +8 -0
- package/src/view/minimap/Minimap.d.ts.map +1 -1
- package/src/view/minimap/Minimap.js +16 -3
- package/src/view/minimap/dom/MinimapMarkerView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapMarkerView.js +3 -1
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import { Color as ThreeColor, Mesh, MeshStandardMaterial, SphereGeometry, WebGLRenderer } from "three";
|
|
2
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
3
|
+
import Entity from "../../ecs/Entity.js";
|
|
4
|
+
import Renderable from "../../ecs/renderable/Renderable.js";
|
|
5
|
+
import RenderSystem from "../../ecs/renderable/RenderSystem.js";
|
|
6
|
+
import { Transform } from "../../ecs/transform/Transform.js";
|
|
7
|
+
import { EngineHarness } from "../../EngineHarness.js";
|
|
8
|
+
import { configureThreeRenderer } from "../GraphicsEngine.js";
|
|
9
|
+
import { load_and_set_cubemap_v0 } from "../load_and_set_cubemap_v0.js";
|
|
10
|
+
import { WebGLContextState } from "./WebGLContextState.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* WebGL context loss / restore harness.
|
|
14
|
+
*
|
|
15
|
+
* Drives the engine by hand (no rAF, so it works in a hidden tab), forces context loss through
|
|
16
|
+
* `WEBGL_lose_context`, and compares the framebuffer before and after restore. Read the report from
|
|
17
|
+
* the console or from `window.__context_loss_report`.
|
|
18
|
+
*
|
|
19
|
+
* Scenarios:
|
|
20
|
+
* - `window.__context_loss_test.run()` single loss/restore cycle with a full pixel A/B
|
|
21
|
+
* - `window.__context_loss_test.repeat(n)` n cycles, watching `renderer.info.memory` for leaks
|
|
22
|
+
*
|
|
23
|
+
* The scene is deliberately static (no water, no behaviours) so that two consecutive frames are
|
|
24
|
+
* pixel-identical; the noise floor between two pre-loss captures is reported so the post-restore
|
|
25
|
+
* difference is interpretable.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const harness = new EngineHarness();
|
|
29
|
+
|
|
30
|
+
const SPHERE_POSITION = new Vector3(0, 2, 0);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Grid resolution for the per-region luminance breakdown
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
const REGION_GRID = 4;
|
|
37
|
+
|
|
38
|
+
const scratch_color = new ThreeColor();
|
|
39
|
+
|
|
40
|
+
const VIEWPORT_WIDTH = 800;
|
|
41
|
+
const VIEWPORT_HEIGHT = 600;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @param {Engine} engine
|
|
46
|
+
* @returns {WebGLRenderer}
|
|
47
|
+
*/
|
|
48
|
+
function get_renderer(engine) {
|
|
49
|
+
return engine.graphics.renderer;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Advance the simulation and draw one frame
|
|
54
|
+
* @param {Engine} engine
|
|
55
|
+
* @param {number} [dt]
|
|
56
|
+
* @returns {number} frame duration in milliseconds
|
|
57
|
+
*/
|
|
58
|
+
function frame(engine, dt = 1 / 60) {
|
|
59
|
+
const t0 = performance.now();
|
|
60
|
+
|
|
61
|
+
engine.ticker.onTick.send1(dt);
|
|
62
|
+
engine.render();
|
|
63
|
+
|
|
64
|
+
return performance.now() - t0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Reads the default framebuffer. Must run in the same task as the draw, the drawing buffer is not
|
|
69
|
+
* preserved across a compositor frame.
|
|
70
|
+
* @param {Engine} engine
|
|
71
|
+
* @returns {{width:number, height:number, mean:number[], luminance:number, regions:number[], hash:number, nonzero:number}}
|
|
72
|
+
*/
|
|
73
|
+
function capture(engine) {
|
|
74
|
+
const renderer = get_renderer(engine);
|
|
75
|
+
const gl = renderer.getContext();
|
|
76
|
+
|
|
77
|
+
renderer.setRenderTarget(null);
|
|
78
|
+
|
|
79
|
+
const width = gl.drawingBufferWidth;
|
|
80
|
+
const height = gl.drawingBufferHeight;
|
|
81
|
+
|
|
82
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
83
|
+
|
|
84
|
+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
|
85
|
+
|
|
86
|
+
const mean = [0, 0, 0, 0];
|
|
87
|
+
const regions = new Array(REGION_GRID * REGION_GRID).fill(0);
|
|
88
|
+
const region_counts = new Array(REGION_GRID * REGION_GRID).fill(0);
|
|
89
|
+
|
|
90
|
+
let nonzero = 0;
|
|
91
|
+
let hash = 0x811c9dc5;
|
|
92
|
+
|
|
93
|
+
for (let y = 0; y < height; y++) {
|
|
94
|
+
const region_y = Math.min(REGION_GRID - 1, Math.floor((y / height) * REGION_GRID));
|
|
95
|
+
|
|
96
|
+
for (let x = 0; x < width; x++) {
|
|
97
|
+
const address = (y * width + x) * 4;
|
|
98
|
+
|
|
99
|
+
const r = pixels[address];
|
|
100
|
+
const g = pixels[address + 1];
|
|
101
|
+
const b = pixels[address + 2];
|
|
102
|
+
const a = pixels[address + 3];
|
|
103
|
+
|
|
104
|
+
mean[0] += r;
|
|
105
|
+
mean[1] += g;
|
|
106
|
+
mean[2] += b;
|
|
107
|
+
mean[3] += a;
|
|
108
|
+
|
|
109
|
+
if (r !== 0 || g !== 0 || b !== 0) {
|
|
110
|
+
nonzero++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Rec.709 luma, close enough for an A/B
|
|
114
|
+
const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
115
|
+
|
|
116
|
+
const region_x = Math.min(REGION_GRID - 1, Math.floor((x / width) * REGION_GRID));
|
|
117
|
+
const region = region_y * REGION_GRID + region_x;
|
|
118
|
+
|
|
119
|
+
regions[region] += luma;
|
|
120
|
+
region_counts[region]++;
|
|
121
|
+
|
|
122
|
+
hash = ((hash ^ r) * 0x01000193) >>> 0;
|
|
123
|
+
hash = ((hash ^ g) * 0x01000193) >>> 0;
|
|
124
|
+
hash = ((hash ^ b) * 0x01000193) >>> 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const pixel_count = width * height;
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < 4; i++) {
|
|
131
|
+
mean[i] = mean[i] / pixel_count;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for (let i = 0; i < regions.length; i++) {
|
|
135
|
+
regions[i] = region_counts[i] === 0 ? 0 : regions[i] / region_counts[i];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
width,
|
|
140
|
+
height,
|
|
141
|
+
mean: mean.map(v => Number(v.toFixed(3))),
|
|
142
|
+
luminance: Number((0.2126 * mean[0] + 0.7152 * mean[1] + 0.0722 * mean[2]).toFixed(3)),
|
|
143
|
+
regions: regions.map(v => Number(v.toFixed(2))),
|
|
144
|
+
hash,
|
|
145
|
+
nonzero: Number((nonzero / pixel_count).toFixed(4))
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
*
|
|
151
|
+
* @param {{luminance:number, regions:number[], hash:number, mean:number[]}} a
|
|
152
|
+
* @param {{luminance:number, regions:number[], hash:number, mean:number[]}} b
|
|
153
|
+
*/
|
|
154
|
+
function compare(a, b) {
|
|
155
|
+
let region_max = 0;
|
|
156
|
+
let region_total = 0;
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < a.regions.length; i++) {
|
|
159
|
+
const d = Math.abs(a.regions[i] - b.regions[i]);
|
|
160
|
+
|
|
161
|
+
region_total += d;
|
|
162
|
+
|
|
163
|
+
if (d > region_max) {
|
|
164
|
+
region_max = d;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
identical: a.hash === b.hash,
|
|
170
|
+
luminance_delta: Number((b.luminance - a.luminance).toFixed(3)),
|
|
171
|
+
region_delta_max: Number(region_max.toFixed(3)),
|
|
172
|
+
region_delta_mean: Number((region_total / a.regions.length).toFixed(3))
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Coarse luminance map of the framebuffer, printable in a console. The harness is expected to run in a
|
|
178
|
+
* hidden tab, where screenshots are not available, and an aggregate number does not tell you *what* went
|
|
179
|
+
* missing.
|
|
180
|
+
* @param {Engine} engine
|
|
181
|
+
* @returns {string}
|
|
182
|
+
*/
|
|
183
|
+
function ascii_image(engine, columns = 40, rows = 20) {
|
|
184
|
+
const RAMP = ' .:-=+*#%@';
|
|
185
|
+
|
|
186
|
+
const renderer = get_renderer(engine);
|
|
187
|
+
const gl = renderer.getContext();
|
|
188
|
+
|
|
189
|
+
renderer.setRenderTarget(null);
|
|
190
|
+
|
|
191
|
+
const width = gl.drawingBufferWidth;
|
|
192
|
+
const height = gl.drawingBufferHeight;
|
|
193
|
+
|
|
194
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
195
|
+
|
|
196
|
+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
|
197
|
+
|
|
198
|
+
const lines = [];
|
|
199
|
+
|
|
200
|
+
// readPixels returns rows bottom-up
|
|
201
|
+
for (let row = rows - 1; row >= 0; row--) {
|
|
202
|
+
let line = '';
|
|
203
|
+
|
|
204
|
+
for (let column = 0; column < columns; column++) {
|
|
205
|
+
const x0 = Math.floor(column * width / columns);
|
|
206
|
+
const x1 = Math.floor((column + 1) * width / columns);
|
|
207
|
+
const y0 = Math.floor(row * height / rows);
|
|
208
|
+
const y1 = Math.floor((row + 1) * height / rows);
|
|
209
|
+
|
|
210
|
+
let sum = 0;
|
|
211
|
+
let count = 0;
|
|
212
|
+
|
|
213
|
+
for (let y = y0; y < y1; y += 3) {
|
|
214
|
+
for (let x = x0; x < x1; x += 3) {
|
|
215
|
+
const address = (y * width + x) * 4;
|
|
216
|
+
|
|
217
|
+
sum += 0.2126 * pixels[address] + 0.7152 * pixels[address + 1] + 0.0722 * pixels[address + 2];
|
|
218
|
+
count++;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const luma = sum / count;
|
|
223
|
+
|
|
224
|
+
line += RAMP[Math.min(RAMP.length - 1, Math.floor((luma / 255) * RAMP.length))];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
lines.push(line);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return lines.join('\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*
|
|
235
|
+
* @param {Engine} engine
|
|
236
|
+
*/
|
|
237
|
+
function snapshot_renderer_state(engine) {
|
|
238
|
+
const renderer = get_renderer(engine);
|
|
239
|
+
const gl = renderer.getContext();
|
|
240
|
+
const info = renderer.info;
|
|
241
|
+
|
|
242
|
+
const clear_color = renderer.getClearColor(scratch_color);
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
context_lost: gl.isContextLost(),
|
|
246
|
+
monitor_state: engine.graphics.context.state,
|
|
247
|
+
monitor_generation: engine.graphics.context.generation,
|
|
248
|
+
monitor_loss_count: engine.graphics.context.loss_count,
|
|
249
|
+
programs: info.programs === null ? -1 : info.programs.length,
|
|
250
|
+
textures: info.memory.textures,
|
|
251
|
+
geometries: info.memory.geometries,
|
|
252
|
+
clear_color: [
|
|
253
|
+
Number(clear_color.r.toFixed(4)),
|
|
254
|
+
Number(clear_color.g.toFixed(4)),
|
|
255
|
+
Number(clear_color.b.toFixed(4))
|
|
256
|
+
],
|
|
257
|
+
clear_alpha: renderer.getClearAlpha(),
|
|
258
|
+
environment_set: engine.graphics.scene.environment !== null
|
|
259
|
+
&& engine.graphics.scene.environment !== undefined,
|
|
260
|
+
gl_error: gl.getError()
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Waits for a single event on the canvas
|
|
266
|
+
* @param {HTMLCanvasElement} canvas
|
|
267
|
+
* @param {string} name
|
|
268
|
+
* @param {number} timeout milliseconds
|
|
269
|
+
* @returns {Promise<number>} milliseconds waited, or -1 on timeout
|
|
270
|
+
*/
|
|
271
|
+
function wait_for_canvas_event(canvas, name, timeout) {
|
|
272
|
+
return new Promise(resolve => {
|
|
273
|
+
const t0 = performance.now();
|
|
274
|
+
|
|
275
|
+
let timeout_handle = -1;
|
|
276
|
+
|
|
277
|
+
const handler = () => {
|
|
278
|
+
clearTimeout(timeout_handle);
|
|
279
|
+
canvas.removeEventListener(name, handler);
|
|
280
|
+
resolve(performance.now() - t0);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
canvas.addEventListener(name, handler, false);
|
|
284
|
+
|
|
285
|
+
timeout_handle = setTimeout(() => {
|
|
286
|
+
canvas.removeEventListener(name, handler);
|
|
287
|
+
resolve(-1);
|
|
288
|
+
}, timeout);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* One loss/restore cycle
|
|
294
|
+
* @param {Engine} engine
|
|
295
|
+
* @param {number} [warmup_frames]
|
|
296
|
+
* @param {number} [lost_frames] frames driven while the context is lost
|
|
297
|
+
* @param {number} [settle_frames] frames driven after restore before the second capture
|
|
298
|
+
*/
|
|
299
|
+
async function run_cycle(engine, { warmup_frames = 30, lost_frames = 5, settle_frames = 20 } = {}) {
|
|
300
|
+
const renderer = get_renderer(engine);
|
|
301
|
+
const canvas = renderer.domElement;
|
|
302
|
+
|
|
303
|
+
const report = {};
|
|
304
|
+
|
|
305
|
+
// ---- baseline -------------------------------------------------------------------------------
|
|
306
|
+
for (let i = 0; i < warmup_frames; i++) {
|
|
307
|
+
frame(engine);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
frame(engine);
|
|
311
|
+
const baseline = capture(engine);
|
|
312
|
+
|
|
313
|
+
frame(engine);
|
|
314
|
+
const baseline_2 = capture(engine);
|
|
315
|
+
|
|
316
|
+
report.baseline = baseline;
|
|
317
|
+
report.noise_floor = compare(baseline, baseline_2);
|
|
318
|
+
report.before = snapshot_renderer_state(engine);
|
|
319
|
+
|
|
320
|
+
// ---- lose ---------------------------------------------------------------------------------
|
|
321
|
+
const lost_promise = wait_for_canvas_event(canvas, 'webglcontextlost', 5000);
|
|
322
|
+
|
|
323
|
+
renderer.forceContextLoss();
|
|
324
|
+
|
|
325
|
+
report.lost_event_delay_ms = Number((await lost_promise).toFixed(2));
|
|
326
|
+
|
|
327
|
+
const lost_frame_errors = [];
|
|
328
|
+
|
|
329
|
+
for (let i = 0; i < lost_frames; i++) {
|
|
330
|
+
try {
|
|
331
|
+
frame(engine);
|
|
332
|
+
} catch (e) {
|
|
333
|
+
lost_frame_errors.push(String(e && e.message ? e.message : e));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
report.frames_while_lost_threw = lost_frame_errors;
|
|
338
|
+
report.while_lost = snapshot_renderer_state(engine);
|
|
339
|
+
|
|
340
|
+
// ---- restore ------------------------------------------------------------------------------
|
|
341
|
+
const restored_promise = wait_for_canvas_event(canvas, 'webglcontextrestored', 10000);
|
|
342
|
+
|
|
343
|
+
renderer.forceContextRestore();
|
|
344
|
+
|
|
345
|
+
const restore_delay = await restored_promise;
|
|
346
|
+
|
|
347
|
+
report.restored_event_delay_ms = Number(restore_delay.toFixed(2));
|
|
348
|
+
report.restored = restore_delay >= 0;
|
|
349
|
+
|
|
350
|
+
if (!report.restored) {
|
|
351
|
+
report.verdict = 'context was NOT restored within 10s';
|
|
352
|
+
return report;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// first frame after restore carries the re-upload + recompile storm
|
|
356
|
+
report.first_frame_ms = Number(frame(engine).toFixed(2));
|
|
357
|
+
const first = capture(engine);
|
|
358
|
+
|
|
359
|
+
const settle_durations = [];
|
|
360
|
+
|
|
361
|
+
for (let i = 0; i < settle_frames; i++) {
|
|
362
|
+
settle_durations.push(frame(engine));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
frame(engine);
|
|
366
|
+
const settled = capture(engine);
|
|
367
|
+
|
|
368
|
+
report.settle_frames_ms = {
|
|
369
|
+
max: Number(Math.max(...settle_durations).toFixed(2)),
|
|
370
|
+
mean: Number((settle_durations.reduce((a, b) => a + b, 0) / settle_durations.length).toFixed(2))
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
report.after = snapshot_renderer_state(engine);
|
|
374
|
+
|
|
375
|
+
report.first_frame_vs_baseline = compare(baseline, first);
|
|
376
|
+
report.settled_vs_baseline = compare(baseline, settled);
|
|
377
|
+
|
|
378
|
+
report.settled = settled;
|
|
379
|
+
|
|
380
|
+
return report;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Loss/restore lifecycle without actually killing the context: the canvas events are dispatched by hand.
|
|
385
|
+
*
|
|
386
|
+
* three.js reacts to the restore event by rebuilding all of its GPU-object bookkeeping, so everything is
|
|
387
|
+
* re-uploaded and every shader recompiled exactly as it would be after a real restore - the difference is
|
|
388
|
+
* that the underlying context stays healthy, which is what makes this runnable in a hidden tab (browsers
|
|
389
|
+
* only restore a genuinely lost context while the page is visible).
|
|
390
|
+
*
|
|
391
|
+
* What this covers: the engine's gate, the fix-up sequence, the environment re-bake, and three's own
|
|
392
|
+
* restore path. What it cannot cover: whether the browser is willing to restore, and how stale GL handles
|
|
393
|
+
* from the dead context behave.
|
|
394
|
+
*
|
|
395
|
+
* @param {Engine} engine
|
|
396
|
+
*/
|
|
397
|
+
function run_synthetic_cycle(engine, { warmup_frames = 40, lost_frames = 5, settle_frames = 10 } = {}) {
|
|
398
|
+
const renderer = get_renderer(engine);
|
|
399
|
+
const canvas = renderer.domElement;
|
|
400
|
+
|
|
401
|
+
const report = {};
|
|
402
|
+
|
|
403
|
+
for (let i = 0; i < warmup_frames; i++) {
|
|
404
|
+
frame(engine);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
frame(engine);
|
|
408
|
+
const baseline = capture(engine);
|
|
409
|
+
|
|
410
|
+
frame(engine);
|
|
411
|
+
report.noise_floor = compare(baseline, capture(engine));
|
|
412
|
+
report.baseline = baseline;
|
|
413
|
+
report.before = snapshot_renderer_state(engine);
|
|
414
|
+
report.baseline_image = ascii_image(engine);
|
|
415
|
+
|
|
416
|
+
// ---- loss -----------------------------------------------------------------------------------
|
|
417
|
+
const frame_index_before = engine.graphics.frameIndex;
|
|
418
|
+
|
|
419
|
+
canvas.dispatchEvent(new Event('webglcontextlost', { cancelable: true }));
|
|
420
|
+
|
|
421
|
+
report.state_while_lost = engine.graphics.context.state;
|
|
422
|
+
|
|
423
|
+
const errors = [];
|
|
424
|
+
|
|
425
|
+
for (let i = 0; i < lost_frames; i++) {
|
|
426
|
+
try {
|
|
427
|
+
frame(engine);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
errors.push(String(e && e.message ? e.message : e));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
report.frames_while_lost_threw = errors;
|
|
434
|
+
// the gate returns before the frame counter is touched, so this proves no drawing happened
|
|
435
|
+
report.frames_drawn_while_lost = engine.graphics.frameIndex - frame_index_before;
|
|
436
|
+
|
|
437
|
+
// ---- restore --------------------------------------------------------------------------------
|
|
438
|
+
canvas.dispatchEvent(new Event('webglcontextrestored'));
|
|
439
|
+
|
|
440
|
+
report.first_frame_ms = Number(frame(engine).toFixed(2));
|
|
441
|
+
const first = capture(engine);
|
|
442
|
+
|
|
443
|
+
for (let i = 0; i < settle_frames; i++) {
|
|
444
|
+
frame(engine);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
frame(engine);
|
|
448
|
+
const settled = capture(engine);
|
|
449
|
+
|
|
450
|
+
report.after = snapshot_renderer_state(engine);
|
|
451
|
+
report.first_frame_vs_baseline = compare(baseline, first);
|
|
452
|
+
report.settled_vs_baseline = compare(baseline, settled);
|
|
453
|
+
report.settled = settled;
|
|
454
|
+
report.settled_image = ascii_image(engine);
|
|
455
|
+
|
|
456
|
+
return report;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Renders the same scene through a brand new renderer on a brand new context.
|
|
461
|
+
*
|
|
462
|
+
* Note that the environment map is expected to differ here: swapping the renderer is not a context
|
|
463
|
+
* restore, so the engine's recovery hook never runs and the prefiltered radiance is never re-baked. The
|
|
464
|
+
* value of this scenario is everything else - geometry, textures, shaders, render targets and the
|
|
465
|
+
* composit chain all have to come back from CPU-side data, and any of them failing to shows up as a
|
|
466
|
+
* difference beyond the known environment delta.
|
|
467
|
+
*
|
|
468
|
+
* This is the closest analogue of a restore that can be produced in a hidden tab while still exercising
|
|
469
|
+
* real GPU work: the object graph is untouched, but the GL-side caches are empty, so everything that
|
|
470
|
+
* cannot be re-derived from CPU-side data shows up as a difference against the baseline.
|
|
471
|
+
*
|
|
472
|
+
* @param {Engine} engine
|
|
473
|
+
*/
|
|
474
|
+
function run_fresh_context(engine, { warmup_frames = 40, settle_frames = 10 } = {}) {
|
|
475
|
+
const graphics = engine.graphics;
|
|
476
|
+
|
|
477
|
+
const report = {};
|
|
478
|
+
|
|
479
|
+
for (let i = 0; i < warmup_frames; i++) {
|
|
480
|
+
frame(engine);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
frame(engine);
|
|
484
|
+
const baseline = capture(engine);
|
|
485
|
+
|
|
486
|
+
frame(engine);
|
|
487
|
+
report.noise_floor = compare(baseline, capture(engine));
|
|
488
|
+
report.baseline = baseline;
|
|
489
|
+
|
|
490
|
+
const previous = graphics.renderer;
|
|
491
|
+
|
|
492
|
+
const canvas = document.createElement('canvas');
|
|
493
|
+
const context = canvas.getContext('webgl2', { antialias: false });
|
|
494
|
+
|
|
495
|
+
const fresh = new WebGLRenderer({
|
|
496
|
+
antialias: true,
|
|
497
|
+
logarithmicDepthBuffer: false,
|
|
498
|
+
canvas,
|
|
499
|
+
context,
|
|
500
|
+
powerPreference: 'high-performance'
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
configureThreeRenderer(fresh);
|
|
504
|
+
|
|
505
|
+
graphics.renderer = fresh;
|
|
506
|
+
|
|
507
|
+
graphics.updateSize();
|
|
508
|
+
|
|
509
|
+
report.first_frame_ms = Number(frame(engine).toFixed(2));
|
|
510
|
+
const first = capture(engine);
|
|
511
|
+
|
|
512
|
+
for (let i = 0; i < settle_frames; i++) {
|
|
513
|
+
frame(engine);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
frame(engine);
|
|
517
|
+
const settled = capture(engine);
|
|
518
|
+
|
|
519
|
+
report.fresh_programs = fresh.info.programs.length;
|
|
520
|
+
report.fresh_textures = fresh.info.memory.textures;
|
|
521
|
+
report.fresh_geometries = fresh.info.memory.geometries;
|
|
522
|
+
report.fresh_draw_calls = fresh.info.render.calls;
|
|
523
|
+
report.fresh_triangles = fresh.info.render.triangles;
|
|
524
|
+
report.settled_image = ascii_image(engine);
|
|
525
|
+
|
|
526
|
+
graphics.renderer = previous;
|
|
527
|
+
|
|
528
|
+
graphics.updateSize();
|
|
529
|
+
|
|
530
|
+
report.first_frame_vs_baseline = compare(baseline, first);
|
|
531
|
+
report.settled_vs_baseline = compare(baseline, settled);
|
|
532
|
+
|
|
533
|
+
// and confirm the shared object graph was not disturbed by the swap
|
|
534
|
+
frame(engine);
|
|
535
|
+
report.restored_original_vs_baseline = compare(baseline, capture(engine));
|
|
536
|
+
|
|
537
|
+
fresh.forceContextLoss();
|
|
538
|
+
fresh.dispose();
|
|
539
|
+
|
|
540
|
+
return report;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
*
|
|
545
|
+
* @param {Engine} engine
|
|
546
|
+
*/
|
|
547
|
+
function install_api(engine) {
|
|
548
|
+
const api = {
|
|
549
|
+
engine,
|
|
550
|
+
|
|
551
|
+
frame: (n = 1) => {
|
|
552
|
+
const durations = [];
|
|
553
|
+
for (let i = 0; i < n; i++) {
|
|
554
|
+
durations.push(frame(engine));
|
|
555
|
+
}
|
|
556
|
+
return durations;
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
capture: () => {
|
|
560
|
+
frame(engine);
|
|
561
|
+
return capture(engine);
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
state: () => snapshot_renderer_state(engine),
|
|
565
|
+
|
|
566
|
+
image: () => ascii_image(engine),
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Loss/restore lifecycle driven by synthetic canvas events. Works in a hidden tab.
|
|
570
|
+
*/
|
|
571
|
+
synthetic(options) {
|
|
572
|
+
const report = run_synthetic_cycle(engine, options);
|
|
573
|
+
|
|
574
|
+
window.__context_loss_report = report;
|
|
575
|
+
|
|
576
|
+
console.log('[context-loss] synthetic report', report);
|
|
577
|
+
console.log('[context-loss] baseline\n' + report.baseline_image);
|
|
578
|
+
console.log('[context-loss] after restore\n' + report.settled_image);
|
|
579
|
+
|
|
580
|
+
return report;
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Same scene, brand new context. Works in a hidden tab.
|
|
585
|
+
*/
|
|
586
|
+
fresh(options) {
|
|
587
|
+
const report = run_fresh_context(engine, options);
|
|
588
|
+
|
|
589
|
+
window.__context_loss_report = report;
|
|
590
|
+
|
|
591
|
+
console.log('[context-loss] fresh-context report', report);
|
|
592
|
+
|
|
593
|
+
return report;
|
|
594
|
+
},
|
|
595
|
+
|
|
596
|
+
async run(options) {
|
|
597
|
+
const report = await run_cycle(engine, options);
|
|
598
|
+
|
|
599
|
+
window.__context_loss_report = report;
|
|
600
|
+
|
|
601
|
+
console.log('[context-loss] report', report);
|
|
602
|
+
|
|
603
|
+
return report;
|
|
604
|
+
},
|
|
605
|
+
|
|
606
|
+
async repeat(count = 3, options) {
|
|
607
|
+
const reports = [];
|
|
608
|
+
|
|
609
|
+
for (let i = 0; i < count; i++) {
|
|
610
|
+
console.log(`[context-loss] cycle ${i + 1}/${count}`);
|
|
611
|
+
reports.push(await run_cycle(engine, options));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const summary = reports.map((r, i) => ({
|
|
615
|
+
cycle: i,
|
|
616
|
+
restored: r.restored,
|
|
617
|
+
first_frame_ms: r.first_frame_ms,
|
|
618
|
+
settled_vs_baseline: r.settled_vs_baseline,
|
|
619
|
+
textures_before: r.before.textures,
|
|
620
|
+
textures_after: r.after === undefined ? -1 : r.after.textures,
|
|
621
|
+
geometries_after: r.after === undefined ? -1 : r.after.geometries,
|
|
622
|
+
programs_after: r.after === undefined ? -1 : r.after.programs
|
|
623
|
+
}));
|
|
624
|
+
|
|
625
|
+
window.__context_loss_report = { reports, summary };
|
|
626
|
+
|
|
627
|
+
console.log('[context-loss] repeat summary', summary);
|
|
628
|
+
|
|
629
|
+
return summary;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
window.__context_loss_test = api;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
*
|
|
638
|
+
* @param {Engine} engine
|
|
639
|
+
* @returns {Promise<void>}
|
|
640
|
+
*/
|
|
641
|
+
async function main(engine) {
|
|
642
|
+
// fixed viewport: the pixel A/B needs a stable resolution, and the harness is expected to run in a
|
|
643
|
+
// hidden tab where the window-driven sizing path produces a 0-sized viewport
|
|
644
|
+
engine.settings.graphics_control_viewport_size.set(false);
|
|
645
|
+
engine.viewStack.size.set(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
|
|
646
|
+
|
|
647
|
+
await EngineHarness.buildBasics({
|
|
648
|
+
engine,
|
|
649
|
+
focus: SPHERE_POSITION,
|
|
650
|
+
distance: 6,
|
|
651
|
+
pitch: 0.35,
|
|
652
|
+
// static scene: animated water would make consecutive frames differ
|
|
653
|
+
enableWater: false,
|
|
654
|
+
enableTerrain: true,
|
|
655
|
+
terrainSize: { x: 32, y: 32 },
|
|
656
|
+
enableLights: true,
|
|
657
|
+
enableShadows: true,
|
|
658
|
+
cameraController: false
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const ecd = engine.entityManager.dataset;
|
|
662
|
+
|
|
663
|
+
// a large mirror-metal sphere; almost all of its shading comes from scene.environment, which makes
|
|
664
|
+
// the PMREM environment map's survival directly visible in the pixel A/B
|
|
665
|
+
const material = new MeshStandardMaterial({
|
|
666
|
+
color: 0xffffff,
|
|
667
|
+
metalness: 1,
|
|
668
|
+
roughness: 0.08
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
const mesh = new Mesh(new SphereGeometry(2, 48, 32), material);
|
|
672
|
+
|
|
673
|
+
mesh.castShadow = true;
|
|
674
|
+
|
|
675
|
+
new Entity()
|
|
676
|
+
.add(new Renderable(mesh))
|
|
677
|
+
.add(Transform.fromJSON({
|
|
678
|
+
position: SPHERE_POSITION,
|
|
679
|
+
scale: { x: 1, y: 1, z: 1 }
|
|
680
|
+
}))
|
|
681
|
+
.build(ecd);
|
|
682
|
+
|
|
683
|
+
await load_and_set_cubemap_v0(engine.graphics, 'data/textures/cubemaps/hip_miramar/32/', '.png');
|
|
684
|
+
|
|
685
|
+
install_api(engine);
|
|
686
|
+
|
|
687
|
+
console.log('[context-loss] ready. Run window.__context_loss_test.run()');
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
harness.initialize({
|
|
691
|
+
configuration(config, engine) {
|
|
692
|
+
config.addManySystems(
|
|
693
|
+
new RenderSystem(engine.graphics)
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
}).then(main);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CameraSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../../../../src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js"],"names":[],"mappings":"AAeA;;IAEI,qBAAe;IAGf;;;;OAIG;IACH,uCAFW,MAAM,QAmBhB;IAED;;;;OAIG;IACH,yCAFW,MAAM,QAmBhB;CACJ;gDAnE+C,mEAAmE;uBAC5F,cAAc"}
|