@next2d/builder 0.1.8 → 0.1.10

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.
Files changed (67) hide show
  1. package/dist/cli.d.ts +59 -0
  2. package/dist/cli.js +141 -0
  3. package/dist/constants.d.ts +53 -0
  4. package/dist/constants.js +64 -0
  5. package/dist/context.d.ts +29 -0
  6. package/dist/context.js +19 -0
  7. package/dist/electron.d.ts +10 -0
  8. package/dist/electron.js +142 -0
  9. package/dist/index.js +18 -790
  10. package/dist/native.d.ts +27 -0
  11. package/dist/native.js +123 -0
  12. package/dist/utils.d.ts +27 -0
  13. package/dist/utils.js +40 -0
  14. package/dist/web.d.ts +18 -0
  15. package/dist/web.js +70 -0
  16. package/dist/xbox.d.ts +62 -0
  17. package/dist/xbox.js +426 -0
  18. package/package.json +11 -9
  19. package/templates/xbox/CMakeLists.txt +47 -11
  20. package/templates/xbox/README.md +18 -0
  21. package/templates/xbox/js/bootstrap.js +120 -0
  22. package/templates/xbox/src/AssetLoader.cpp +115 -12
  23. package/templates/xbox/src/AssetLoader.h +4 -0
  24. package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
  25. package/templates/xbox/src/EmbeddedAssets.h +60 -0
  26. package/templates/xbox/src/EventLoop.cpp +0 -15
  27. package/templates/xbox/src/bindings/Audio.cpp +118 -22
  28. package/templates/xbox/src/bindings/Canvas.cpp +32 -2
  29. package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
  30. package/templates/xbox/src/bindings/Console.cpp +6 -1
  31. package/templates/xbox/src/bindings/DomShims.cpp +18 -2
  32. package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
  33. package/templates/xbox/src/bindings/Image.cpp +83 -57
  34. package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
  35. package/templates/xbox/src/bindings/RasterCore.h +105 -1
  36. package/templates/xbox/src/bindings/Video.cpp +57 -3
  37. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
  38. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
  39. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
  40. package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
  41. package/templates/xbox/src/main.cpp +385 -16
  42. package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
  43. package/templates/xbox/src/platform/AudioDecoder.h +18 -0
  44. package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
  45. package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
  46. package/templates/xbox/src/platform/DecodeQueue.h +27 -0
  47. package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
  48. package/templates/xbox/src/platform/ImageDecoder.h +18 -0
  49. package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
  50. package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
  51. package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
  52. package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
  53. package/templates/xbox/src/v8/CodeCache.h +223 -0
  54. package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
  55. package/templates/xbox/src/v8/V8Util.h +54 -4
  56. package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
  57. package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
  58. package/templates/xbox/tests/check_local.sh +5 -1
  59. package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
  60. package/templates/xbox/tests/raster_test.cpp +50 -0
  61. package/templates/xbox/tests/windows_platform_test.cpp +75 -3
  62. package/templates/xbox/third_party/LICENSES.md +14 -0
  63. package/templates/xbox/third_party/dr_mp3.h +5412 -0
  64. package/templates/xbox/third_party/dr_wav.h +9131 -0
  65. package/templates/xbox/third_party/stb_image.h +7988 -0
  66. package/templates/xbox/third_party/stb_truetype.h +5079 -0
  67. package/templates/xbox/third_party/stb_vorbis.c +5584 -0
@@ -4,10 +4,12 @@
4
4
  #include "AssetLoader.h"
5
5
  #include "EventTarget.h"
6
6
  #include "platform/AudioEngine.h"
7
+ #include "platform/DecodeQueue.h"
7
8
  #include "v8/V8Util.h"
8
9
  #include "v8/WeakHandle.h"
9
10
 
10
11
  #include <memory>
12
+ #include <utility>
11
13
  #include <vector>
12
14
 
13
15
  namespace next2d {
@@ -230,20 +232,37 @@ void PumpAudioEvents(v8::Isolate* isolate)
230
232
  if (sources.empty()) return;
231
233
  v8::HandleScope scope(isolate);
232
234
  v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
235
+
236
+ // 1) 終了した source を集めて先に登録リストから外す。ここでは JS を呼ばない。
237
+ // (この時点の erase による v8::Global の move は他の再入が無いので安全)
238
+ std::vector<v8::Global<v8::Object>> finished;
233
239
  for (size_t i = 0; i < sources.size();) {
234
240
  v8::Local<v8::Object> node = sources[i].Get(isolate);
235
241
  auto voice = Get<AudioVoice>(node);
236
242
  if (!voice || voice->IsFinished()) {
237
- if (voice) {
238
- v8::Local<v8::Object> ev = v8::Object::New(isolate);
239
- ev->Set(ctx, Str(isolate, "type"), Str(isolate, "ended")).Check();
240
- DispatchEvent(isolate, node, ev);
241
- }
243
+ finished.push_back(std::move(sources[i]));
242
244
  sources.erase(sources.begin() + i);
243
245
  } else {
244
246
  ++i;
245
247
  }
246
248
  }
249
+
250
+ // 2) リストが安定した状態で "ended" を配送する。
251
+ // "ended" ハンドラは source.disconnect() や新規再生で PlayingSources を
252
+ // 再入的に変更しうる。以前は配送と erase を同一ループで行っていたため、
253
+ // 配送中にベクタが変更されると直後の erase 中の v8::Global move が壊れ
254
+ // GlobalHandles::MoveGlobal でアクセス違反していた。独立した finished を
255
+ // 走査することで再入変更から隔離する。
256
+ for (auto& g : finished) {
257
+ v8::Local<v8::Object> node = g.Get(isolate);
258
+ auto voice = Get<AudioVoice>(node);
259
+ if (voice) {
260
+ v8::Local<v8::Object> ev = v8::Object::New(isolate);
261
+ ev->Set(ctx, Str(isolate, "type"), Str(isolate, "ended")).Check();
262
+ DispatchEvent(isolate, node, ev);
263
+ }
264
+ g.Reset();
265
+ }
247
266
  }
248
267
 
249
268
  namespace {
@@ -266,21 +285,49 @@ void DecodeAudioData(const v8::FunctionCallbackInfo<v8::Value>& args)
266
285
  static_cast<uint8_t*>(store->Data()) + store->ByteLength());
267
286
  }
268
287
 
288
+ // Media Foundation デコードはバックグラウンドスレッドへ (Promise なので元々
289
+ // 非同期契約)。BGM の同期デコードは画面遷移時の数百 ms ヒッチになっていた。
290
+ auto resolver_ref =
291
+ std::make_shared<v8::Global<v8::Promise::Resolver>>(isolate, resolver);
269
292
  auto pcm = std::make_shared<PcmBuffer>();
270
- if (input.empty() || !AudioEngine::Decode(input, *pcm)) {
271
- resolver->Reject(ctx, v8::Exception::Error(Str(isolate, "audio decode failed"))).Check();
272
- return;
273
- }
293
+ auto decoded = std::make_shared<bool>(false);
294
+
295
+ decodequeue::Submit(
296
+ [input = std::move(input), pcm, decoded]() {
297
+ *decoded = !input.empty() && AudioEngine::Decode(input, *pcm);
298
+ },
299
+ [isolate, resolver_ref, pcm, decoded]() {
300
+ v8::HandleScope hs(isolate);
301
+ v8::Local<v8::Promise::Resolver> r = resolver_ref->Get(isolate);
302
+ v8::Local<v8::Context> c = r->GetCreationContextChecked();
303
+ v8::Context::Scope cs(c);
304
+
305
+ // 重要: デコード失敗でも Reject しない。@next2d/media の SoundDecodeService は
306
+ // decodeAudioData の reject を catch して先頭バイトをスキップし再帰リトライするが、
307
+ // そのループ条件 (`idx > byteLength`) が常に false のため同一バッファで無限再帰し、
308
+ // マイクロタスクが枯渇してフレームワークのローディング画面ごとフリーズする。
309
+ // ここで無音の空バッファを resolve すれば、そのサウンドは無音になるだけでアプリは
310
+ // 進行できる (graceful degradation)。実データが壊れているか MF 非対応かは上のログで判別。
311
+ if (!*decoded) {
312
+ pcm->samples.clear(); // 0 サンプル = 無音
313
+ pcm->channels = pcm->channels ? pcm->channels : 2;
314
+ pcm->sample_rate = pcm->sample_rate ? pcm->sample_rate : 48000;
315
+ }
274
316
 
275
- v8::Local<v8::Object> buffer = InternalTemplate(isolate)->NewInstance(ctx).ToLocalChecked();
276
- Attach<PcmBuffer>(isolate, buffer, pcm);
277
- v8util::SetValue(isolate, buffer, "sampleRate", v8::Number::New(isolate, pcm->sample_rate));
278
- v8util::SetValue(isolate, buffer, "numberOfChannels", v8::Integer::New(isolate, pcm->channels));
279
- v8util::SetValue(isolate, buffer, "duration",
280
- v8::Number::New(isolate, pcm->channels
281
- ? static_cast<double>(pcm->samples.size()) / pcm->channels / pcm->sample_rate
282
- : 0.0));
283
- resolver->Resolve(ctx, buffer).Check();
317
+ v8::Local<v8::Object> buffer =
318
+ InternalTemplate(isolate)->NewInstance(c).ToLocalChecked();
319
+ Attach<PcmBuffer>(isolate, buffer, pcm);
320
+ v8util::SetValue(isolate, buffer, "sampleRate",
321
+ v8::Number::New(isolate, pcm->sample_rate));
322
+ v8util::SetValue(isolate, buffer, "numberOfChannels",
323
+ v8::Integer::New(isolate, pcm->channels));
324
+ v8util::SetValue(isolate, buffer, "duration",
325
+ v8::Number::New(isolate, pcm->channels
326
+ ? static_cast<double>(pcm->samples.size()) / pcm->channels / pcm->sample_rate
327
+ : 0.0));
328
+ r->Resolve(c, buffer).Check();
329
+ resolver_ref->Reset();
330
+ });
284
331
  }
285
332
 
286
333
  // --- Audio (HTMLAudioElement 最小実装) -------------------------------------
@@ -335,17 +382,49 @@ void AudioElementConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
335
382
  v8::Local<v8::Object> self = args.This();
336
383
  Attach<AudioVoice>(isolate, self, nullptr);
337
384
 
338
- // URL 指定があれば同期でロード+デコードして voice を作る (失敗時は無音の no-op)
385
+ // URL 指定があればロードし、デコードはバックグラウンドスレッドへ (失敗時は
386
+ // 無音の no-op)。デコード完了前に play() が呼ばれていた場合 (paused=false) は
387
+ // 完了時に再生を開始する (同期デコード時代の「即時再生」挙動を維持)。
339
388
  if (args.Length() > 0 && args[0]->IsString()) {
340
389
  const std::string src = ToStdString(isolate, args[0]);
341
390
  v8util::SetValue(isolate, self, "src", args[0]);
342
391
  HostContext* host = HostContext::From(isolate);
343
392
  auto bytes = host->assets->ReadBinary(src);
344
393
  if (bytes) {
394
+ auto self_ref = std::make_shared<v8::Global<v8::Object>>(isolate, self);
345
395
  auto pcm = std::make_shared<PcmBuffer>();
346
- if (AudioEngine::Decode(*bytes, *pcm)) {
347
- Attach<AudioVoice>(isolate, self, host->audio->CreateVoice(pcm));
348
- }
396
+ auto decoded = std::make_shared<bool>(false);
397
+ decodequeue::Submit(
398
+ [input = std::move(*bytes), pcm, decoded]() {
399
+ *decoded = AudioEngine::Decode(input, *pcm);
400
+ },
401
+ [isolate, self_ref, pcm, decoded, host]() {
402
+ v8::HandleScope hs(isolate);
403
+ v8::Local<v8::Object> el = self_ref->Get(isolate);
404
+ v8::Local<v8::Context> c = el->GetCreationContextChecked();
405
+ v8::Context::Scope cs(c);
406
+ if (*decoded) {
407
+ auto voice = host->audio->CreateVoice(pcm);
408
+ Attach<AudioVoice>(isolate, el, voice);
409
+ // デコード中に play() 済みなら開始する
410
+ v8::Local<v8::Value> paused_v, loop_v, vol_v;
411
+ const bool paused =
412
+ !el->Get(c, Str(isolate, "paused")).ToLocal(&paused_v) ||
413
+ paused_v->BooleanValue(isolate);
414
+ if (!paused && voice) {
415
+ if (el->Get(c, Str(isolate, "volume")).ToLocal(&vol_v) &&
416
+ vol_v->IsNumber()) {
417
+ voice->SetVolume(static_cast<float>(
418
+ vol_v.As<v8::Number>()->Value()));
419
+ }
420
+ const bool loop =
421
+ el->Get(c, Str(isolate, "loop")).ToLocal(&loop_v) &&
422
+ loop_v->BooleanValue(isolate);
423
+ voice->Start(loop);
424
+ }
425
+ }
426
+ self_ref->Reset();
427
+ });
349
428
  }
350
429
  }
351
430
 
@@ -374,6 +453,23 @@ void AudioContextConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
374
453
  v8util::SetMethod(isolate, self, "createBufferSource", CreateBufferSource);
375
454
  v8util::SetMethod(isolate, self, "createGain", CreateGain);
376
455
  v8util::SetMethod(isolate, self, "decodeAudioData", DecodeAudioData);
456
+
457
+ // state / resume / suspend / close: ブラウザの AudioContext はユーザージェスチャまで
458
+ // suspended だが、ホストの XAudio2 は常に有効なので "running" 固定。player の音声
459
+ // アンロック (初回 pointerup で ctx.resume()) が未実装だと TypeError で throw し、
460
+ // そのハンドラ(ミュート解除等)以降が中断する。解決済み Promise を返す no-op にする。
461
+ v8util::SetValue(isolate, self, "state", v8util::Str(isolate, "running"));
462
+ auto resolvedPromise = [](const v8::FunctionCallbackInfo<v8::Value>& a) {
463
+ v8::Isolate* iso = a.GetIsolate();
464
+ v8::Local<v8::Context> c = iso->GetCurrentContext();
465
+ auto r = v8::Promise::Resolver::New(c).ToLocalChecked();
466
+ (void) r->Resolve(c, v8::Undefined(iso));
467
+ a.GetReturnValue().Set(r->GetPromise());
468
+ };
469
+ v8util::SetMethod(isolate, self, "resume", resolvedPromise);
470
+ v8util::SetMethod(isolate, self, "suspend", resolvedPromise);
471
+ v8util::SetMethod(isolate, self, "close", resolvedPromise);
472
+
377
473
  args.GetReturnValue().Set(self);
378
474
  }
379
475
 
@@ -105,6 +105,16 @@ void TransferControlToOffscreen(const v8::FunctionCallbackInfo<v8::Value>& args)
105
105
  if (args.This()->Get(ctx, Str(isolate, "height")).ToLocal(&hv) && hv->IsNumber()) {
106
106
  height = static_cast<int>(hv.As<v8::Number>()->Value());
107
107
  }
108
+
109
+ // このソース canvas (args.This()) こそがプレイヤーの主表示 canvas。
110
+ // player は「document.createElement('canvas') → pointer リスナ登録 → その canvas を
111
+ // transferControlToOffscreen()」の順で主 canvas を作る (input と描画で同一要素)。
112
+ // 一方 createElement は毎回 main_canvas を上書きするため、後続のヒットテスト用
113
+ // Canvas2D 生成 (getContext('2d') 用に 7 個も作る) で main_canvas が別要素に化け、
114
+ // WndProc の入力配送先とリスナ登録先がズレて「ボタンが全く反応しない」状態になる。
115
+ // transferControlToOffscreen が呼ばれる唯一の canvas = 正しい入力要素なので確定させる。
116
+ host->main_canvas.Reset(isolate, args.This());
117
+
108
118
  args.GetReturnValue().Set(CreateOffscreenCanvas(isolate, host, width, height));
109
119
  }
110
120
 
@@ -147,6 +157,18 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
147
157
  SetValue(isolate, canvas, "style", v8::Object::New(isolate));
148
158
  SetMethod(isolate, canvas, "getContext", Canvas_GetContext);
149
159
  SetMethod(isolate, canvas, "transferControlToOffscreen", TransferControlToOffscreen);
160
+ // Pointer capture: player の pointerdown ハンドラは t.setPointerCapture(pointerId) を
161
+ // 呼ぶ。未実装だと TypeError で DispatchEvent の TryCatch に飲まれ、以降の座標解決
162
+ // (dl) に到達せずボタンが反応しない。no-op として実装する (単一ウィンドウでは
163
+ // capture の有無で挙動は変わらない)。hasPointerCapture は false を返す。
164
+ SetMethod(isolate, canvas, "setPointerCapture",
165
+ [](const v8::FunctionCallbackInfo<v8::Value>&) {});
166
+ SetMethod(isolate, canvas, "releasePointerCapture",
167
+ [](const v8::FunctionCallbackInfo<v8::Value>&) {});
168
+ SetMethod(isolate, canvas, "hasPointerCapture",
169
+ [](const v8::FunctionCallbackInfo<v8::Value>& a) {
170
+ a.GetReturnValue().Set(false);
171
+ });
150
172
  InstallEventTarget(isolate, canvas); // pointer/wheel リスナを保持
151
173
  SetMethod(isolate, canvas, "getBoundingClientRect", GetBoundingClientRect);
152
174
 
@@ -191,8 +213,16 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
191
213
  (void) attrs.As<v8::Object>()->Delete(c, a[0]);
192
214
  }
193
215
  });
194
- // 主要 canvas を HostContext から参照できるよう記録 (WndProc の入力配送先)
195
- HostContext::From(isolate)->main_canvas.Reset(isolate, canvas);
216
+ // 主要 canvas の記録 (WndProc の入力配送先)。
217
+ // 確定は transferControlToOffscreen 側で行う (player が input を張る主 canvas はそちら)。
218
+ // ここでは「まだ未設定なら暫定で最初の canvas を採用」するフォールバックに留め、
219
+ // 後続の Canvas2D 生成で上書きしない (上書きすると入力配送先がヒットテスト用 canvas に化ける)。
220
+ {
221
+ HostContext* host = HostContext::From(isolate);
222
+ if (host->main_canvas.IsEmpty()) {
223
+ host->main_canvas.Reset(isolate, canvas);
224
+ }
225
+ }
196
226
  return canvas;
197
227
  }
198
228