-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathSceneRuntimeImpl.cs
More file actions
190 lines (157 loc) · 6.58 KB
/
Copy pathSceneRuntimeImpl.cs
File metadata and controls
190 lines (157 loc) · 6.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
using CrdtEcsBridge.PoolsProviders;
using Cysharp.Threading.Tasks;
using DCL.Diagnostics;
using DCL.Utilities.Extensions;
using Microsoft.ClearScript;
using Microsoft.ClearScript.JavaScript;
using Microsoft.ClearScript.V8;
using SceneRunner.Scene;
using SceneRuntime.Apis;
using SceneRuntime.Apis.Modules.EngineApi;
using SceneRuntime.ModuleHub;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine.Assertions;
namespace SceneRuntime
{
// Avoid the same name for Namespace and Class
public sealed class SceneRuntimeImpl : ISceneRuntime, IJsOperations
{
internal readonly V8ScriptEngine engine;
private readonly ScriptObject arrayCtor;
private readonly ScriptObject unit8ArrayCtor;
private readonly List<ITypedArray<byte>> uint8Arrays;
private readonly JsApiBunch jsApiBunch;
// ResetableSource is an optimization to reduce 11kb of memory allocation per Update (reduces 15kb to 4kb per update)
private readonly JSTaskResolverResetable resetableSource;
private readonly CancellationTokenSource isDisposingTokenSource = new ();
private int nextUint8Array;
private ScriptObject updateFunc;
private ScriptObject startFunc;
private EngineApiWrapper? engineApi;
public V8RuntimeHeapInfo RuntimeHeapInfo { get; private set; }
public IJsOperations JsOperations => this;
CancellationTokenSource ISceneRuntime.isDisposingTokenSource => isDisposingTokenSource;
public SceneRuntimeImpl(
string sourceCode,
string initCode,
IReadOnlyDictionary<string, string> jsModules,
SceneShortInfo sceneShortInfo,
V8EngineFactory engineFactory
)
{
resetableSource = new JSTaskResolverResetable();
engine = engineFactory.Create(sceneShortInfo);
jsApiBunch = new JsApiBunch(engine);
var moduleHub = new SceneModuleHub(engine);
moduleHub.LoadAndCompileJsModules(jsModules);
// Compile Scene Code
V8Script sceneScript = engine.Compile(sourceCode).EnsureNotNull();
// Initialize init API
// TODO: This is only needed for the LifeCycle
var unityOpsApi = new UnityOpsApi(engine, moduleHub, sceneScript, sceneShortInfo);
engine.AddHostObject("UnityOpsApi", unityOpsApi);
// engine.Execute(initCode.validateCode!);
engine.Execute(initCode);
// Set global SDK configuration flags
engine.Execute("globalThis.ENABLE_SDK_TWEEN_SEQUENCE = false;");
// Setup unitask resolver
engine.AddHostObject("__resetableSource", resetableSource);
arrayCtor = (ScriptObject)engine.Global.GetProperty("Array");
unit8ArrayCtor = (ScriptObject)engine.Global.GetProperty("Uint8Array");
uint8Arrays = new List<ITypedArray<byte>>();
nextUint8Array = 0;
}
/// <remarks>
/// <see cref="SceneFacade" /> is a component in the global scene as an
/// <see cref="ISceneFacade" />. It owns its <see cref="SceneRuntimeImpl" /> through its
/// <see cref="deps" /> field, which in turns owns its <see cref="V8ScriptEngine" />. So that also
/// shall be the chain of Dispose calls.
/// </remarks>
public void Dispose()
{
engine.Dispose();
jsApiBunch.Dispose();
}
public void ExecuteSceneJson()
{
engine.Execute(@"
const __internalScene = require('~scene.js')
const __internalOnStart = async function () {
try {
await __internalScene.onStart()
__resetableSource.Completed()
} catch (e) {
__resetableSource.Reject(e.stack)
}
}
const __internalOnUpdate = async function (dt) {
try {
await __internalScene.onUpdate(dt)
__resetableSource.Completed()
} catch(e) {
__resetableSource.Reject(e.stack)
}
}
");
updateFunc = (ScriptObject)engine.Evaluate("__internalOnUpdate").EnsureNotNull();
startFunc = (ScriptObject)engine.Evaluate("__internalOnStart").EnsureNotNull();
}
public void OnSceneIsCurrentChanged(bool isCurrent)
{
jsApiBunch.OnSceneIsCurrentChanged(isCurrent);
}
public void RegisterEngineAPIWrapper(EngineApiWrapper newWrapper)
{
engineApi = newWrapper;
}
public void Register<T>(string itemName, T target) where T: JsApiWrapper
{
jsApiBunch.AddHostObject(itemName, target);
}
public void SetIsDisposing()
{
isDisposingTokenSource.Cancel();
isDisposingTokenSource.Dispose();
}
public void Interrupt()
{
// V8ScriptEngine.Interrupt is thread-safe; causes ScriptInterruptedException
// to be thrown from any active JS execution on this engine.
try { engine.Interrupt(); }
catch (ObjectDisposedException) { /* engine already disposed — nothing to do */ }
}
public UniTask StartScene()
{
resetableSource.Reset();
startFunc.InvokeAsFunction();
return resetableSource.Task;
}
public UniTask UpdateScene(float dt)
{
nextUint8Array = 0;
RuntimeHeapInfo = engine.GetRuntimeHeapInfo();
resetableSource.Reset();
updateFunc.InvokeAsFunction(dt);
return resetableSource.Task;
}
public void ApplyStaticMessages(ReadOnlyMemory<byte> data)
{
PoolableByteArray result = engineApi.EnsureNotNull().api.CrdtSendToRenderer(data, false);
// Initial messages are not expected to return anything
Assert.IsTrue(result.IsEmpty);
}
ScriptObject IJsOperations.NewArray() =>
(ScriptObject)arrayCtor.Invoke(true);
ITypedArray<byte> IJsOperations.NewUint8Array(int length) =>
(ITypedArray<byte>)unit8ArrayCtor.Invoke(true, length);
ITypedArray<byte> IJsOperations.GetTempUint8Array()
{
if (nextUint8Array >= uint8Arrays.Count)
uint8Arrays.Add((ITypedArray<byte>)unit8ArrayCtor.Invoke(true,
IJsOperations.LIVEKIT_MAX_SIZE));
return uint8Arrays[nextUint8Array++];
}
}
}