@next2d/builder 0.1.7 → 0.1.8

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 (32) hide show
  1. package/dist/index.js +62 -26
  2. package/package.json +3 -2
  3. package/templates/xbox/CMakeLists.txt +69 -0
  4. package/templates/xbox/README.md +16 -8
  5. package/templates/xbox/cmake/FetchDawn.cmake +47 -6
  6. package/templates/xbox/cmake/FindV8.cmake +15 -0
  7. package/templates/xbox/js/bootstrap.js +31 -28
  8. package/templates/xbox/js/selftest.js +740 -90
  9. package/templates/xbox/src/EventLoop.cpp +28 -1
  10. package/templates/xbox/src/EventLoop.h +12 -0
  11. package/templates/xbox/src/bindings/Audio.cpp +99 -15
  12. package/templates/xbox/src/bindings/Bindings.h +10 -0
  13. package/templates/xbox/src/bindings/Canvas.cpp +45 -0
  14. package/templates/xbox/src/bindings/Canvas2D.cpp +2 -5
  15. package/templates/xbox/src/bindings/DomShims.cpp +127 -0
  16. package/templates/xbox/src/bindings/EventTarget.cpp +2 -0
  17. package/templates/xbox/src/bindings/Fetch.cpp +2 -8
  18. package/templates/xbox/src/bindings/Image.cpp +212 -10
  19. package/templates/xbox/src/bindings/Polyfills.cpp +791 -0
  20. package/templates/xbox/src/bindings/Video.cpp +96 -7
  21. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +815 -15
  22. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +3 -8
  23. package/templates/xbox/src/gpu/DawnContext.cpp +90 -3
  24. package/templates/xbox/src/gpu/DawnContext.h +18 -0
  25. package/templates/xbox/src/main.cpp +88 -6
  26. package/templates/xbox/src/platform/AudioEngine.cpp +9 -3
  27. package/templates/xbox/src/v8/V8Runtime.cpp +69 -5
  28. package/templates/xbox/src/v8/V8Util.h +29 -0
  29. package/templates/xbox/src/v8/WeakHandle.h +45 -0
  30. package/templates/xbox/src/worker/WorkerRuntime.cpp +325 -41
  31. package/templates/xbox/src/worker/WorkerRuntime.h +20 -10
  32. package/templates/xbox/tests/check_local.sh +2 -1
package/dist/index.js CHANGED
@@ -170,14 +170,14 @@ const loadConfig = async () => {
170
170
  */
171
171
  const buildWeb = () => {
172
172
  return new Promise((resolve, reject) => {
173
- const stream = cp.spawn("npx", [
173
+ const stream = $spawn("npx", [
174
174
  "@next2d/vite-plugin-next2d-auto-loader"
175
175
  ], { "stdio": "inherit" });
176
176
  stream.on("close", (code) => {
177
177
  if (code !== 0) {
178
178
  reject("vite plugin command failed.");
179
179
  }
180
- const stream = cp.spawn("npx", [
180
+ const stream = $spawn("npx", [
181
181
  "vite",
182
182
  "--outDir",
183
183
  $buildDir,
@@ -207,7 +207,7 @@ const installElectron = () => {
207
207
  if (fs.existsSync(`${process.cwd()}/electron/node_modules`)) {
208
208
  return resolve();
209
209
  }
210
- const stream = cp.spawn("npm", [
210
+ const stream = $spawn("npm", [
211
211
  "--prefix",
212
212
  `${process.cwd()}/electron`,
213
213
  "install",
@@ -283,7 +283,7 @@ const buildSteam = async () => {
283
283
  // copy HTML, JavaScript
284
284
  await copyResources();
285
285
  if (preview) {
286
- cp.spawn("npx", [
286
+ $spawn("npx", [
287
287
  "electron",
288
288
  `${process.cwd()}/electron/index.js`
289
289
  ], { "stdio": "inherit" });
@@ -341,7 +341,7 @@ const generateNativeProject = () => {
341
341
  if (fs.existsSync(`${process.cwd()}/${platform}`)) {
342
342
  return resolve();
343
343
  }
344
- const stream = cp.spawn("npx", [
344
+ const stream = $spawn("npx", [
345
345
  "cap",
346
346
  "add",
347
347
  platform
@@ -373,7 +373,7 @@ const runNative = async () => {
373
373
  const config = JSON.parse(fs.readFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, { "encoding": "utf8" }));
374
374
  config.webDir = `${$outDir}/${platformDir}/${environment}/`;
375
375
  fs.writeFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, JSON.stringify(config, null, 2));
376
- cp.spawn("npx", [
376
+ $spawn("npx", [
377
377
  "cap",
378
378
  "run",
379
379
  platform
@@ -396,7 +396,7 @@ const openNative = async () => {
396
396
  const config = JSON.parse(fs.readFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, { "encoding": "utf8" }));
397
397
  config.webDir = `${$outDir}/${platformDir}/${environment}/`;
398
398
  fs.writeFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, JSON.stringify(config, null, 2));
399
- const stream = cp.spawn("npx", [
399
+ const stream = $spawn("npx", [
400
400
  "cap",
401
401
  "sync",
402
402
  platform
@@ -406,7 +406,7 @@ const openNative = async () => {
406
406
  console.log(pc.red(`Failed to sync ${platform} project.`));
407
407
  return;
408
408
  }
409
- cp.spawn("npx", [
409
+ $spawn("npx", [
410
410
  "cap",
411
411
  "open",
412
412
  platform
@@ -430,7 +430,7 @@ const buildNative = async () => {
430
430
  const config = JSON.parse(fs.readFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, { "encoding": "utf8" }));
431
431
  config.webDir = `${$outDir}/${platformDir}/${environment}/`;
432
432
  fs.writeFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, JSON.stringify(config, null, 2));
433
- const stream = cp.spawn("npx", [
433
+ const stream = $spawn("npx", [
434
434
  "cap",
435
435
  "sync",
436
436
  platform
@@ -440,7 +440,7 @@ const buildNative = async () => {
440
440
  console.log(pc.red(`Failed to sync ${platform} project.`));
441
441
  return;
442
442
  }
443
- cp.spawn("npx", [
443
+ $spawn("npx", [
444
444
  "cap",
445
445
  "build",
446
446
  platform
@@ -471,6 +471,28 @@ const getTemplateDir = (name) => {
471
471
  * @constant
472
472
  */
473
473
  const XBOX_CONFIG_NAME = "MicrosoftGame.config";
474
+ /**
475
+ * @description npx / npm を子プロセスとして起動する (クロスプラットフォーム)。
476
+ * Windows では npx/npm が .cmd (バッチファイル) のため、shell 経由で
477
+ * ないと起動できない (spawn ENOENT / Node の CVE-2024-27980 対応により
478
+ * .cmd の直接 spawn も不可)。スペースを含む引数は二重引用符で保護する。
479
+ * Spawn npx/npm cross-platform. On Windows they are .cmd batch files,
480
+ * which require a shell to spawn.
481
+ *
482
+ * @param {string} command
483
+ * @param {array} args
484
+ * @param {object} options
485
+ * @return {object}
486
+ * @method
487
+ * @public
488
+ */
489
+ const $spawn = (command, args, options = {}) => {
490
+ if (process.platform === "win32" && (command === "npx" || command === "npm")) {
491
+ const quoted = args.map((a) => /\s/.test(a) ? `"${a}"` : a);
492
+ return cp.spawn(command, quoted, { ...options, "shell": true });
493
+ }
494
+ return cp.spawn(command, args, options);
495
+ };
474
496
  /**
475
497
  * @description 自動ダウンロードする prebuilt V8 のバージョン。
476
498
  * `.github/workflows/build-v8.yml` で発行した Releases のタグと、
@@ -481,7 +503,18 @@ const XBOX_CONFIG_NAME = "MicrosoftGame.config";
481
503
  * @type {string}
482
504
  * @constant
483
505
  */
484
- const XBOX_V8_VERSION = "13.6.233.17";
506
+ const XBOX_V8_VERSION = "13.7.152.19";
507
+ /**
508
+ * @description prebuilt V8 のアセットリビジョン。同一 V8 バージョンでも
509
+ * gn フラグ変更 (例: r2 = WebAssembly + DrumBrake 有効化) で
510
+ * バイナリが変わる場合に上げ、Release タグとローカルキャッシュを分離する。
511
+ * Asset revision. Bump when gn flags change for the same V8 version
512
+ * (r2 = WebAssembly + DrumBrake enabled).
513
+ *
514
+ * @type {string}
515
+ * @constant
516
+ */
517
+ const XBOX_V8_REVISION = "r3";
485
518
  /**
486
519
  * @description ゲーム側の `xbox/` へスキャフォールドしないテンプレート内ファイル。
487
520
  * - MicrosoftGame.config : ゲーム固有設定 (injectGameConfig が注入)
@@ -620,13 +653,13 @@ const resolveXboxV8Root = async () => {
620
653
  const cacheBase = process.env.LOCALAPPDATA
621
654
  ? `${process.env.LOCALAPPDATA}/next2d`
622
655
  : `${os.homedir()}/.cache/next2d`;
623
- const cacheDir = `${cacheBase}/v8/${XBOX_V8_VERSION}`;
656
+ const cacheDir = `${cacheBase}/v8/${XBOX_V8_VERSION}-${XBOX_V8_REVISION}`;
624
657
  if (fs.existsSync(`${cacheDir}/include/v8.h`)) {
625
658
  return cacheDir;
626
659
  }
627
660
  // 4. GitHub Releases から自動ダウンロード (マシンごとに初回のみ)
628
661
  const assetName = `v8-monolith-${XBOX_V8_VERSION}-windows-x64.zip`;
629
- const url = `https://github.com/Next2D/builder/releases/download/v8-${XBOX_V8_VERSION}-windows-x64/${assetName}`;
662
+ const url = `https://github.com/Next2D/builder/releases/download/v8-${XBOX_V8_VERSION}-windows-x64-${XBOX_V8_REVISION}/${assetName}`;
630
663
  console.log(pc.green(`Downloading prebuilt V8 ${XBOX_V8_VERSION} (first time only) ...`));
631
664
  console.log(url);
632
665
  const response = await fetch(url);
@@ -731,19 +764,22 @@ const buildXbox = async () => {
731
764
  ], { "stdio": "inherit" });
732
765
  return;
733
766
  }
734
- // フルビルド (Release パッケージまで)
735
- const stream = cp.spawn("cmake", [
736
- "--build", cmakeBuildDir,
737
- "--config", "Release"
738
- ], { "stdio": "inherit" });
739
- stream.on("close", (code) => {
740
- if (code !== 0) {
741
- console.log(pc.red("Export of the Xbox (GDK) package failed."));
742
- return;
743
- }
744
- console.log();
745
- console.log(pc.green(`Finished building the Xbox (GDK) package for ${gdkArch}.`));
746
- console.log();
767
+ // フルビルド (Release パッケージまで)。失敗時は reject して非ゼロ終了させる
768
+ // (以前は表示のみで exit code 0 のまま成功扱いになっていた)
769
+ await new Promise((resolve, reject) => {
770
+ const stream = cp.spawn("cmake", [
771
+ "--build", cmakeBuildDir,
772
+ "--config", "Release"
773
+ ], { "stdio": "inherit" });
774
+ stream.on("close", (code) => {
775
+ if (code !== 0) {
776
+ return reject("Export of the Xbox (GDK) package failed.");
777
+ }
778
+ console.log();
779
+ console.log(pc.green(`Finished building the Xbox (GDK) package for ${gdkArch}.`));
780
+ console.log();
781
+ resolve();
782
+ });
747
783
  });
748
784
  };
749
785
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next2d/builder",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Multi-platform builder for Next2d Framework, supporting export to various platforms such as macOS, Windows, iOS, Android and Web(HTML)",
5
5
  "author": "Toshiyuki Ienaga <ienaga@next2d.app>",
6
6
  "license": "MIT",
@@ -22,6 +22,7 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "lint": "eslint src/index.ts",
25
+ "prepare": "tsc",
25
26
  "release": "tsc"
26
27
  },
27
28
  "files": [
@@ -39,7 +40,7 @@
39
40
  "@electron-forge/core": "^7.11.2",
40
41
  "@types/node": "^26.1.0",
41
42
  "picocolors": "^1.1.1",
42
- "vite": "^8.1.2"
43
+ "vite": "^8.1.3"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@eslint/eslintrc": "^3.3.5",
@@ -23,6 +23,18 @@ set(CMAKE_CXX_STANDARD 20)
23
23
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
24
24
  set(CMAKE_CXX_EXTENSIONS OFF)
25
25
 
26
+ if(MSVC)
27
+ # MSVC は /Zc:__cplusplus を渡さないと __cplusplus を 199711L のまま報告し
28
+ # (/std:c++20 を指定していても)、v8config.h の C++20 チェックで停止する。
29
+ add_compile_options(/Zc:__cplusplus)
30
+ endif()
31
+
32
+ # CRT は静的 (/MT, Debug は /MTd) に統一する。
33
+ # prebuilt V8 monolith は GN の既定で静的 CRT ビルドのため、ホスト側が既定の /MD だと
34
+ # libcpmt.lib と msvcprt.lib が衝突して LNK2005/LNK2038 でリンクに失敗する。
35
+ # ここで設定することで FetchContent の Dawn も同じ CRT でビルドされる。
36
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
37
+
26
38
  # GDK target platform (Gaming.Xbox.* / Gaming.Desktop.*) の判定
27
39
  if(NOT DEFINED CMAKE_GENERATOR_PLATFORM)
28
40
  message(WARNING "CMAKE_GENERATOR_PLATFORM が未設定です。builder は -A Gaming.Xbox.Scarlett.x64 等を渡します。")
@@ -35,6 +47,28 @@ else()
35
47
  set(NEXT2D_IS_CONSOLE OFF)
36
48
  endif()
37
49
 
50
+ # -----------------------------------------------------------------------------
51
+ # GDK の解決 (2通り)
52
+ # A) インストール済み GDK: -A Gaming.Desktop.x64 / Gaming.Xbox.* でビルド。
53
+ # VS 統合が include/lib を自動供給するため追加設定は不要。
54
+ # B) 展開レイアウト (インストーラ不要・CI 向け): NuGet の Microsoft.GDK.Windows を
55
+ # 展開し、-A x64 + GDK_ROOT (cmake 変数 or 環境変数) で指定する。
56
+ # GDK_ROOT は <package>/native/<edition>/windows を指す (include/, lib/x64/, bin/x64/)。
57
+ # -----------------------------------------------------------------------------
58
+ string(FIND "${CMAKE_GENERATOR_PLATFORM}" "Gaming." _is_gaming_platform)
59
+ if(_is_gaming_platform LESS 0)
60
+ if(NOT DEFINED GDK_ROOT)
61
+ set(GDK_ROOT "$ENV{GDK_ROOT}")
62
+ endif()
63
+ if(NOT GDK_ROOT OR NOT EXISTS "${GDK_ROOT}/include/XGameRuntime.h")
64
+ message(FATAL_ERROR
65
+ "GDK が見つかりません。次のいずれかで指定してください:\n"
66
+ " 1) GDK をインストールして -A Gaming.Desktop.x64 でビルド\n"
67
+ " 2) NuGet の Microsoft.GDK.Windows を展開し、-A x64 で\n"
68
+ " -D GDK_ROOT=<package>/native/<edition>/windows (または環境変数 GDK_ROOT) を指定")
69
+ endif()
70
+ endif()
71
+
38
72
  # -----------------------------------------------------------------------------
39
73
  # 依存: Dawn (WebGPU 実装 / D3D12 バックエンド)
40
74
  # -----------------------------------------------------------------------------
@@ -61,11 +95,46 @@ target_include_directories(Next2DXboxHost PRIVATE
61
95
  "${CMAKE_CURRENT_SOURCE_DIR}/src"
62
96
  )
63
97
 
98
+ # 展開レイアウト (GDK_ROOT) 使用時: include/lib を明示指定し、実行時 DLL を並べる
99
+ if(_is_gaming_platform LESS 0)
100
+ target_include_directories(Next2DXboxHost PRIVATE "${GDK_ROOT}/include")
101
+ target_link_directories(Next2DXboxHost PRIVATE "${GDK_ROOT}/lib/x64")
102
+ add_custom_command(TARGET Next2DXboxHost POST_BUILD
103
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
104
+ "${GDK_ROOT}/bin/x64/xgameruntime.dll"
105
+ "$<TARGET_FILE_DIR:Next2DXboxHost>"
106
+ COMMENT "Staging xgameruntime.dll next to the executable")
107
+ endif()
108
+
64
109
  target_link_libraries(Next2DXboxHost PRIVATE
65
110
  v8::v8
66
111
  dawn::webgpu_dawn
67
112
  )
68
113
 
114
+ if(MSVC)
115
+ # CRT は静的 (/MT) に統一している (V8 monolith が静的 CRT 固定のため)。
116
+ # 一方 GDK の xgameruntime.lib 等は /MD ビルドで /DEFAULTLIB:msvcprt を
117
+ # 持ち込み LNK2005 (多重定義) を起こすので、動的 C++ ランタイムを除外して
118
+ # 静的側 (libcpmt) に一本化する。
119
+ target_link_options(Next2DXboxHost PRIVATE
120
+ /NODEFAULTLIB:msvcprt.lib
121
+ /NODEFAULTLIB:msvcprtd.lib
122
+ )
123
+
124
+ # Release でも PDB を生成する (クラッシュダンプ解析でスタックを名前解決するため)
125
+ target_compile_options(Next2DXboxHost PRIVATE /Zi)
126
+ target_link_options(Next2DXboxHost PRIVATE /DEBUG /OPT:REF /OPT:ICF)
127
+ endif()
128
+
129
+ # webgpu_dawn は SHARED (DLL)。実行に必要なため exe の隣へステージする
130
+ if(TARGET webgpu_dawn)
131
+ add_custom_command(TARGET Next2DXboxHost POST_BUILD
132
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
133
+ "$<TARGET_FILE:webgpu_dawn>"
134
+ "$<TARGET_FILE_DIR:Next2DXboxHost>"
135
+ COMMENT "Staging webgpu_dawn.dll next to the executable")
136
+ endif()
137
+
69
138
  # WebGPU / D3D12 / GameInput / WIC / XAudio2 / GDK
70
139
  target_link_libraries(Next2DXboxHost PRIVATE
71
140
  d3d12
@@ -51,7 +51,7 @@ C++ 実行ファイルをビルドします。Electron や WebView は使用し
51
51
  ### ランタイム環境の要点
52
52
 
53
53
  - **jitless V8**: Xbox/Switch のリテールは JIT(動的コード生成)を禁止するため、V8 は
54
- `--jitless`(Ignition インタプリタのみ)で起動する。WebAssembly も無効。
54
+ `--jitless --wasm-jitless` で起動する (JS=Ignition インタプリタ、wasm=DrumBrake インタープリタ)。
55
55
  - **Worker / OffscreenCanvas**: player はレンダラを Worker + `transferControlToOffscreen`
56
56
  で動かす。本ホストは **「同一 Isolate 内の別 Context を協調スケジューリングする単一スレッド
57
57
  Worker モデル」** を採用(`src/worker/WorkerRuntime`)。`postMessage` は V8 の
@@ -73,7 +73,7 @@ C++ 実行ファイルをビルドします。Electron や WebView は使用し
73
73
  |------|------|
74
74
  | OS | Windows 11 |
75
75
  | IDE | Visual Studio 2022 (v143, C++ Game development workload) |
76
- | GDK | Microsoft GDK (June 2024 以降) — ID@Xbox 登録が必要 |
76
+ | GDK | 公開版 GDK をインストール (`-A Gaming.Desktop.x64`)、**または** NuGet `Microsoft.GDK.Windows` を展開して `-A x64` + 環境変数/`-D` `GDK_ROOT=<pkg>/native/<edition>/windows`(インストーラ不要・CI 向け)。コンソール向け GDKX は ID@Xbox 登録が必要 |
77
77
  | CMake | 3.26 以降 |
78
78
  | V8 | 不要 (builder が prebuilt を自動ダウンロード)。自前ビルドは `--v8-root` で指定可 |
79
79
  | Dawn | CMake の FetchContent で自動取得(ネットワーク必須) |
@@ -120,9 +120,9 @@ set DEPOT_TOOLS_WIN_TOOLCHAIN=0
120
120
  fetch v8
121
121
  cd v8
122
122
  :: CI のコンパイル検証と同じバージョンに固定 (xbox-host-ci.yml の V8_TAG)
123
- git checkout 13.6.233.17
123
+ git checkout 13.7.152.19
124
124
  gclient sync
125
- gn gen out\x64.release --args="v8_monolithic=true v8_use_external_startup_data=false is_component_build=false use_custom_libcxx=false icu_use_data_file=false treat_warnings_as_errors=false v8_jitless=true v8_enable_turbofan=false v8_enable_webassembly=false target_cpu=\"x64\" is_debug=false v8_enable_pointer_compression=true v8_enable_sandbox=false"
125
+ gn gen out\x64.release --args="v8_monolithic=true v8_use_external_startup_data=false is_component_build=false use_custom_libcxx=false icu_use_data_file=false treat_warnings_as_errors=false v8_enable_drumbrake=true target_cpu=\"x64\" is_debug=false v8_enable_pointer_compression=true v8_enable_sandbox=false"
126
126
  ninja -C out\x64.release v8_monolith
127
127
  ```
128
128
 
@@ -142,9 +142,12 @@ ninja -C out\x64.release v8_monolith
142
142
  > - `treat_warnings_as_errors=false` を推奨。`use_custom_libcxx=false` では MSVC STL の
143
143
  > ヘッダ警告が -Werror で停止することがあります。**VS のバージョンは VS2022 (v143) を推奨**
144
144
  > (新しすぎる STL は V8 の clang が警告/エラーを出しやすい)。
145
- > - `v8_jitless=true` + `v8_enable_turbofan=false` + `v8_enable_webassembly=false` を推奨。
146
- > ホストは実行時 `--jitless` で動かすため (Xbox は JIT 禁止)、JIT/wasm をビルドから
147
- > 外すのが正しい構成です (BUILD.gn の assert もこの組み合わせを要求)。ビルドも大幅に速くなります。
145
+ > - `v8_enable_drumbrake=true` は必須 (r2)。**DrumBrake = V8 の wasm インタープリタ**で、
146
+ > JIT 禁止環境 (コンソール) でも wasm を解釈実行できます。wasm はビルド時に turbofan を
147
+ > 要求するため `v8_jitless` ビルドは使えません — JIT の無効化はホストが実行時フラグ
148
+ > `--jitless --wasm-jitless` で行います (JS=Ignition / wasm=DrumBrake)。
149
+ > 注意: DrumBrake は既定ビルド対象外のためタグによりコンパイル不能な場合があります
150
+ > (13.6.233.17 は不可 → 13.7.152.19 で修正済み)。
148
151
  > - pointer compression は `CMakeLists.txt` の `V8_COMPRESS_POINTERS` と一致させてください
149
152
  > (sandbox 無効に合わせ `V8_ENABLE_SANDBOX` は定義しません)。不一致は ABI 崩れの原因になります。
150
153
  > - 上記は PC (`Gaming.Desktop.x64`) 用。コンソール実機 (`Gaming.Xbox.*`) 向けは
@@ -210,7 +213,7 @@ macOS / Linux でそのまま実行できます:
210
213
  tests/check_local.sh
211
214
  ```
212
215
 
213
- 構文チェックは実 V8 ヘッダ (pin: `13.6.233.17`) に対して、Windows API 非依存の
216
+ 構文チェックは実 V8 ヘッダ (pin: `13.7.152.19`) に対して、Windows API 非依存の
214
217
  V8 バインディング 12 ファイルをコンパイル検証します (API 削除・型不整合を検出)。
215
218
 
216
219
  ### 2. GitHub Actions (windows-latest) — `.github/workflows/xbox-host-ci.yml`
@@ -302,6 +305,11 @@ player の実 API 使用を全面調査し、以下を実装済み。**この環
302
305
  | ポインタ/キーボード/ホイール入力 | ⚠️ 実装(要検証) | `WndProc`→`DispatchEvent`。配送先canvasの特定は実機検証 |
303
306
  | 動画 (HTMLVideoElement) | ⚠️ 実装(要検証) | `Video.cpp`(Media Foundation)。フレーム精度/音声同期は実機検証 |
304
307
  | クリップボード (`navigator.clipboard`) | ✅ 実装 | Win32 クリップボード (readText/writeText) |
308
+ | **JS エンジン/グローバル機能** (player 全数調査) | ✅ 実装 | Proxy=V8コア。`crypto.randomUUID`/`getRandomValues`(BCrypt)、`TextEncoder`/`TextDecoder`/`queueMicrotask`/`location`/`atob`/`btoa` は worker 含む全コンテキストへ注入。`createImageBitmap` は ImageData/ImageBitmap/canvas/Blob を受理 |
309
+ | **WebAssembly** | ✅ 実装 (要 V8 r2) | prebuilt V8 r2 = wasm + **DrumBrake**(V8 の wasm インタープリタ、JIT 禁止コンソール向け)。実行時 `--jitless --wasm-jitless` で JS=Ignition / wasm=DrumBrake 解釈実行 |
310
+ | **indexedDB** (最小実装) | ✅ 実装 | open/createObjectStore/transaction/get/put/delete/clear/getAll/count/keyPath。値は JSON 化可能なもの + ArrayBuffer/TypedArray(base64 永続化)。カーソル/インデックスは未対応。保存先 `%LOCALAPPDATA%\Next2D\idb_<db>.json` |
311
+ | **localStorage / sessionStorage** | ✅ 実装 | JS セマンティクス + ファイル永続化 (`%LOCALAPPDATA%\Next2D\`)。コンソール実機では XGameSave 化が必要 («EXTEND») |
312
+ | **Audio 要素** (`new Audio(url)`) | ✅ 実装 | play/pause/paused/loop/volume/currentTime/preload (pause→play は先頭から) |
305
313
 
306
314
  > player が使用する API はコード上すべて実装済み。残る唯一の障壁は
307
315
  > **GDK/devkit を要するコンパイル・実機検証**(この環境では物理的に不可)。
@@ -3,13 +3,18 @@
3
3
  # D3D12 バックエンドのみを有効化し、Xbox(GDK)/PC(GDK) で利用する。
4
4
  #
5
5
  # 注意:
6
- # - Dawn のビルドには depot_tools 由来の一部が不要になる CMake パスを使用する。
6
+ # - submodule はクローンしない (GIT_SUBMODULES "")。SwiftShader→LLVM まで再帰して
7
+ # 数 GB のクローン + Windows の MAX_PATH 超過 ("Filename too long") で失敗するため。
8
+ # 依存は Dawn 公式の DAWN_FETCH_DEPENDENCIES (python スクリプト) が必要分だけ取得する。
9
+ # - DAWN_FETCH_DEPENDENCIES には python3 が PATH に必要。
7
10
  # - 初回構成はネットワークとビルド時間を要する。CI ではキャッシュ推奨。
8
- # - DAWN_TAG は動作確認済みタグに固定すること (chromium ブランチに追従)。
11
+ # - DAWN_TAG はバインディング (bindings/webgpu/*) が使う API 世代と一致させること:
12
+ # wgpu::Limits / ShaderSourceWGSL / SurfaceSourceWindowsHWND / OptionalBool
13
+ # (いずれも 2025 以降の命名。古いタグでは SupportedLimits 等になりビルド不可)
9
14
  # =============================================================================
10
15
  include(FetchContent)
11
16
 
12
- set(DAWN_TAG "chromium/6478" CACHE STRING "Dawn のチェックアウトタグ")
17
+ set(DAWN_TAG "v20260402.171122" CACHE STRING "Dawn のチェックアウトタグ (github.com/google/dawn)")
13
18
 
14
19
  # Dawn のビルドオプション: 必要なものだけを有効化
15
20
  set(DAWN_FETCH_DEPENDENCIES ON CACHE BOOL "" FORCE)
@@ -24,14 +29,24 @@ set(DAWN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
24
29
  set(TINT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
25
30
  set(TINT_BUILD_CMD_TOOLS OFF CACHE BOOL "" FORCE)
26
31
 
27
- # webgpu_dawn: webgpu.h C-API を提供する統合ライブラリ (バインディング層はこれを叩く)
28
- set(DAWN_BUILD_MONOLITHIC_LIBRARY ON CACHE BOOL "" FORCE)
32
+ # webgpu_dawn: webgpu.h C-API を提供する統合ライブラリ (バインディング層はこれを叩く)。
33
+ # 2025 以降の Dawn では SHARED / STATIC / OFF の3値。
34
+ # SHARED (DLL) を選ぶ理由: V8 monolith と Dawn は双方が abseil を静的に内蔵しており、
35
+ # STATIC だと raw_hash_set 等の同名シンボルが LNK2005 で衝突する (しかも absl の
36
+ # バージョンが異なるため /FORCE:MULTIPLE は実行時破壊のリスクがある)。
37
+ # DLL にすれば公開されるのは WebGPU C API のみで、absl は DLL 内部に閉じる。
38
+ set(DAWN_BUILD_MONOLITHIC_LIBRARY SHARED CACHE STRING "" FORCE)
39
+ set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
29
40
 
30
41
  FetchContent_Declare(
31
42
  dawn
32
- GIT_REPOSITORY https://dawn.googlesource.com/dawn
43
+ # GitHub ミラーの方が CI から高速・安定 (googlesource はレート制限がきつい)
44
+ GIT_REPOSITORY https://github.com/google/dawn.git
33
45
  GIT_TAG ${DAWN_TAG}
34
46
  GIT_SHALLOW TRUE
47
+ # submodule は取得しない (DAWN_FETCH_DEPENDENCIES が必要分を取得する)
48
+ GIT_SUBMODULES ""
49
+ GIT_SUBMODULES_RECURSE FALSE
35
50
  )
36
51
 
37
52
  FetchContent_MakeAvailable(dawn)
@@ -40,3 +55,29 @@ FetchContent_MakeAvailable(dawn)
40
55
  if(NOT TARGET dawn::webgpu_dawn AND TARGET webgpu_dawn)
41
56
  add_library(dawn::webgpu_dawn ALIAS webgpu_dawn)
42
57
  endif()
58
+
59
+ # -----------------------------------------------------------------------------
60
+ # CRT を静的 (/MT) へ強制統一する。
61
+ # CMAKE_MSVC_RUNTIME_LIBRARY 変数だけでは Dawn の third_party (fetch_dawn_dependencies
62
+ # が取得する abseil/protobuf 等) の一部に伝播せず /MD が混ざり、静的 CRT 固定の
63
+ # prebuilt V8 とのリンクで LNK2005/LNK2019 になる。全ターゲットへ再帰的に
64
+ # プロパティを直接設定して確実に揃える。
65
+ # -----------------------------------------------------------------------------
66
+ function(next2d_force_static_crt dir)
67
+ get_property(_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
68
+ foreach(_t IN LISTS _targets)
69
+ get_target_property(_type ${_t} TYPE)
70
+ if(NOT _type STREQUAL "INTERFACE_LIBRARY")
71
+ set_property(TARGET ${_t} PROPERTY
72
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
73
+ endif()
74
+ endforeach()
75
+ get_property(_subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
76
+ foreach(_sd IN LISTS _subdirs)
77
+ next2d_force_static_crt("${_sd}")
78
+ endforeach()
79
+ endfunction()
80
+
81
+ if(MSVC)
82
+ next2d_force_static_crt("${dawn_SOURCE_DIR}")
83
+ endif()
@@ -40,6 +40,21 @@ if(NOT V8_ROOT OR NOT EXISTS "${V8_ROOT}")
40
40
  "詳細は README.md の『V8 の用意』を参照。")
41
41
  endif()
42
42
 
43
+ # find_path/find_library の結果は CMakeCache に永続化されるため、
44
+ # V8_ROOT の変更 (バージョン更新等) や参照先の消滅に追随しない。
45
+ # 古い/無効な検出結果はここで破棄して再探索させる。
46
+ if(DEFINED CACHE{NEXT2D_V8_ROOT_LAST} AND NOT "$CACHE{NEXT2D_V8_ROOT_LAST}" STREQUAL "${V8_ROOT}")
47
+ unset(V8_INCLUDE_DIR CACHE)
48
+ unset(V8_MONOLITH_LIB CACHE)
49
+ endif()
50
+ if(V8_INCLUDE_DIR AND NOT EXISTS "${V8_INCLUDE_DIR}/v8.h")
51
+ unset(V8_INCLUDE_DIR CACHE)
52
+ endif()
53
+ if(V8_MONOLITH_LIB AND NOT EXISTS "${V8_MONOLITH_LIB}")
54
+ unset(V8_MONOLITH_LIB CACHE)
55
+ endif()
56
+ set(NEXT2D_V8_ROOT_LAST "${V8_ROOT}" CACHE INTERNAL "直近の構成で使った V8_ROOT")
57
+
43
58
  find_path(V8_INCLUDE_DIR
44
59
  NAMES v8.h
45
60
  PATHS "${V8_ROOT}/include"
@@ -25,7 +25,7 @@
25
25
  // V8 単体には Web の Text* が含まれないため最小実装を提供する。
26
26
  if (typeof global.TextEncoder === "undefined") {
27
27
  global.TextEncoder = class TextEncoder {
28
- get encoding() { return "utf-8"; }
28
+ get encoding() { return "utf-8" }
29
29
  encode(str) {
30
30
  str = String(str);
31
31
  const out = [];
@@ -34,23 +34,23 @@
34
34
  if (code < 0x80) {
35
35
  out.push(code);
36
36
  } else if (code < 0x800) {
37
- out.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
37
+ out.push(0xc0 | code >> 6, 0x80 | code & 0x3f);
38
38
  } else if (code >= 0xd800 && code <= 0xdbff) {
39
39
  // サロゲートペア
40
40
  const hi = code;
41
41
  const lo = str.charCodeAt(++i);
42
- code = 0x10000 + ((hi - 0xd800) << 10) + (lo - 0xdc00);
42
+ code = 0x10000 + (hi - 0xd800 << 10) + (lo - 0xdc00);
43
43
  out.push(
44
- 0xf0 | (code >> 18),
45
- 0x80 | ((code >> 12) & 0x3f),
46
- 0x80 | ((code >> 6) & 0x3f),
47
- 0x80 | (code & 0x3f)
44
+ 0xf0 | code >> 18,
45
+ 0x80 | code >> 12 & 0x3f,
46
+ 0x80 | code >> 6 & 0x3f,
47
+ 0x80 | code & 0x3f
48
48
  );
49
49
  } else {
50
50
  out.push(
51
- 0xe0 | (code >> 12),
52
- 0x80 | ((code >> 6) & 0x3f),
53
- 0x80 | (code & 0x3f)
51
+ 0xe0 | code >> 12,
52
+ 0x80 | code >> 6 & 0x3f,
53
+ 0x80 | code & 0x3f
54
54
  );
55
55
  }
56
56
  }
@@ -61,10 +61,10 @@
61
61
 
62
62
  if (typeof global.TextDecoder === "undefined") {
63
63
  global.TextDecoder = class TextDecoder {
64
- constructor(label) { this._label = label || "utf-8"; }
65
- get encoding() { return "utf-8"; }
64
+ constructor(label) { this._label = label || "utf-8" }
65
+ get encoding() { return "utf-8" }
66
66
  decode(input) {
67
- if (!input) return "";
67
+ if (!input) { return "" }
68
68
  const bytes = input instanceof Uint8Array
69
69
  ? input
70
70
  : new Uint8Array(input.buffer || input);
@@ -75,14 +75,14 @@
75
75
  if (c < 0x80) {
76
76
  out += String.fromCharCode(c);
77
77
  } else if (c < 0xe0) {
78
- out += String.fromCharCode(((c & 0x1f) << 6) | (bytes[i++] & 0x3f));
78
+ out += String.fromCharCode((c & 0x1f) << 6 | bytes[i++] & 0x3f);
79
79
  } else if (c < 0xf0) {
80
80
  out += String.fromCharCode(
81
- ((c & 0x0f) << 12) | ((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f)
81
+ (c & 0x0f) << 12 | (bytes[i++] & 0x3f) << 6 | bytes[i++] & 0x3f
82
82
  );
83
83
  } else {
84
- let code = ((c & 0x07) << 18) | ((bytes[i++] & 0x3f) << 12) |
85
- ((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f);
84
+ let code = (c & 0x07) << 18 | (bytes[i++] & 0x3f) << 12 |
85
+ (bytes[i++] & 0x3f) << 6 | bytes[i++] & 0x3f;
86
86
  code -= 0x10000;
87
87
  out += String.fromCharCode(0xd800 + (code >> 10), 0xdc00 + (code & 0x3ff));
88
88
  }
@@ -97,28 +97,31 @@
97
97
  if (typeof global.URL === "undefined") {
98
98
  global.URL = class URL {
99
99
  constructor(url, base) {
100
- this.href = base ? (String(base).replace(/\/?$/, "/") + url) : String(url);
100
+ this.href = base ? String(base).replace(/\/?$/, "/") + url : String(url);
101
101
  this.pathname = this.href.replace(/^[a-z]+:\/\/[^/]*/i, "");
102
102
  this.search = "";
103
103
  this.hash = "";
104
104
  }
105
- toString() { return this.href; }
105
+ toString() { return this.href }
106
106
  };
107
- global.URL.createObjectURL = function () { return ""; };
107
+ global.URL.createObjectURL = function () { return "" };
108
+ // 意図的な no-op スタブ (ローカル実行では解放処理は不要)
109
+ // eslint-disable-next-line no-empty-function
108
110
  global.URL.revokeObjectURL = function () {};
109
111
  }
110
112
 
111
113
  // --- location (Next2D が参照する場合の最小値) ---------------------------
112
114
  if (typeof global.location === "undefined") {
113
115
  global.location = {
114
- href: "app://local/",
115
- origin: "app://local",
116
- protocol: "app:",
117
- host: "local",
118
- hostname: "local",
119
- pathname: "/",
120
- search: "",
121
- hash: "",
116
+ "href": "app://local/",
117
+ "origin": "app://local",
118
+ "protocol": "app:",
119
+ "host": "local",
120
+ "hostname": "local",
121
+ "pathname": "/",
122
+ "search": "",
123
+ "hash": "",
124
+ // eslint-disable-next-line no-empty-function
122
125
  reload() {}
123
126
  };
124
127
  }