@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.
- package/dist/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -790
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +11 -9
- package/templates/xbox/CMakeLists.txt +47 -11
- package/templates/xbox/README.md +18 -0
- package/templates/xbox/js/bootstrap.js +120 -0
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +0 -15
- package/templates/xbox/src/bindings/Audio.cpp +118 -22
- package/templates/xbox/src/bindings/Canvas.cpp +32 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +18 -2
- package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
- package/templates/xbox/src/bindings/Image.cpp +83 -57
- package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +57 -3
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
- package/templates/xbox/src/main.cpp +385 -16
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
- package/templates/xbox/src/v8/V8Util.h +54 -4
- package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
- package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
- package/templates/xbox/tests/check_local.sh +5 -1
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <cctype>
|
|
8
8
|
#include <cmath>
|
|
9
9
|
#include <cstdint>
|
|
10
|
+
#include <memory>
|
|
10
11
|
#include <string>
|
|
11
12
|
#include <utility>
|
|
12
13
|
#include <vector>
|
|
@@ -26,10 +27,18 @@ struct SubPath {
|
|
|
26
27
|
|
|
27
28
|
struct RGBA { uint8_t r=0,g=0,b=0,a=255; };
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
+
// クリップ領域 (デバイス座標)。
|
|
31
|
+
// active=false … 無制限
|
|
32
|
+
// active=true, mask=null … 矩形クリップ [x0,x1) x [y0,y1) (厳密)
|
|
33
|
+
// mask!=null … 任意形状クリップ。x0..y1 は外接矩形 (高速リジェクト用)、
|
|
34
|
+
// mask は surface と同寸のカバレッジ (1=内側/0=外側)。
|
|
35
|
+
// mask は shared_ptr のため save()/restore() の state コピーで安価に共有される
|
|
36
|
+
// (clip() は既存 mask を破壊せず新しい mask を割り当てるので復元が壊れない)。
|
|
30
37
|
struct ClipRect {
|
|
31
38
|
bool active = false;
|
|
32
39
|
double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
|
40
|
+
std::shared_ptr<const std::vector<uint8_t>> mask;
|
|
41
|
+
int mask_w = 0, mask_h = 0;
|
|
33
42
|
};
|
|
34
43
|
|
|
35
44
|
// RGBA8 のピクセルバッファ。
|
|
@@ -101,6 +110,9 @@ inline void Blend(Surface& s, const ClipRect& clip, int x, int y, const RGBA& co
|
|
|
101
110
|
{
|
|
102
111
|
if (x < 0 || y < 0 || x >= s.width || y >= s.height) return;
|
|
103
112
|
if (clip.active && (x < clip.x0 || x >= clip.x1 || y < clip.y0 || y >= clip.y1)) return;
|
|
113
|
+
// 任意形状クリップ: マスクの外なら書き込まない。
|
|
114
|
+
if (clip.mask && (x >= clip.mask_w || y >= clip.mask_h ||
|
|
115
|
+
(*clip.mask)[static_cast<size_t>(y) * clip.mask_w + x] == 0)) return;
|
|
104
116
|
size_t i = (static_cast<size_t>(y) * s.width + x) * 4;
|
|
105
117
|
double a = (col.a / 255.0) * alpha;
|
|
106
118
|
s.pixels[i+0] = static_cast<uint8_t>(col.r * a + s.pixels[i+0] * (1 - a));
|
|
@@ -215,6 +227,98 @@ inline void IntersectClip(ClipRect& clip, double nx0, double ny0, double nx1, do
|
|
|
215
227
|
}
|
|
216
228
|
}
|
|
217
229
|
|
|
230
|
+
// パスが単一の軸並行矩形か判定する (矩形なら外接矩形クリップで厳密に足りる)。
|
|
231
|
+
// beginPath→rect() は 4 or 5 点 (閉路点重複) の矩形サブパスになる。
|
|
232
|
+
inline bool IsAxisAlignedRect(const std::vector<SubPath>& path)
|
|
233
|
+
{
|
|
234
|
+
if (path.size() != 1) return false;
|
|
235
|
+
const std::vector<Point>& p = path[0].pts;
|
|
236
|
+
// 末尾が始点と重複する 5 点表現も許容する
|
|
237
|
+
size_t n = p.size();
|
|
238
|
+
if (n == 5 && std::abs(p[4].x - p[0].x) < 1e-6 && std::abs(p[4].y - p[0].y) < 1e-6) {
|
|
239
|
+
n = 4;
|
|
240
|
+
}
|
|
241
|
+
if (n != 4) return false;
|
|
242
|
+
// 各辺が水平または垂直であること
|
|
243
|
+
for (size_t i = 0; i < 4; ++i) {
|
|
244
|
+
const Point& a = p[i];
|
|
245
|
+
const Point& b = p[(i + 1) % 4];
|
|
246
|
+
const bool horizontal = std::abs(a.y - b.y) < 1e-6;
|
|
247
|
+
const bool vertical = std::abs(a.x - b.x) < 1e-6;
|
|
248
|
+
if (!horizontal && !vertical) return false;
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// パスを nonzero winding でカバレッジマスク (surface 同寸, 1=内側) にラスタライズする。
|
|
254
|
+
// スキャンライン規約は FillPath と一致させる (塗りと同じ形にクリップされる)。
|
|
255
|
+
inline std::shared_ptr<std::vector<uint8_t>> RasterizePathMask(
|
|
256
|
+
const std::vector<SubPath>& path, int width, int height)
|
|
257
|
+
{
|
|
258
|
+
auto mask = std::make_shared<std::vector<uint8_t>>(
|
|
259
|
+
static_cast<size_t>(width) * height, 0);
|
|
260
|
+
if (width <= 0 || height <= 0) return mask;
|
|
261
|
+
|
|
262
|
+
double miny = 1e18, maxy = -1e18;
|
|
263
|
+
for (auto& sp : path) for (auto& p : sp.pts) { miny = std::min(miny,p.y); maxy = std::max(maxy,p.y); }
|
|
264
|
+
if (maxy < miny) return mask;
|
|
265
|
+
int y0 = std::max(0, static_cast<int>(std::floor(miny)));
|
|
266
|
+
int y1 = std::min(height - 1, static_cast<int>(std::ceil(maxy)));
|
|
267
|
+
for (int y = y0; y <= y1; ++y) {
|
|
268
|
+
double sy = y + 0.5;
|
|
269
|
+
std::vector<std::pair<double,int>> xs;
|
|
270
|
+
for (auto& sp : path) {
|
|
271
|
+
size_t n = sp.pts.size();
|
|
272
|
+
if (n < 2) continue;
|
|
273
|
+
for (size_t i = 0; i < n; ++i) {
|
|
274
|
+
Point a = sp.pts[i];
|
|
275
|
+
Point b = sp.pts[(i+1) % n];
|
|
276
|
+
if ((a.y <= sy && b.y > sy) || (b.y <= sy && a.y > sy)) {
|
|
277
|
+
double t = (sy - a.y) / (b.y - a.y);
|
|
278
|
+
xs.push_back({a.x + t * (b.x - a.x), a.y < b.y ? 1 : -1});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
std::sort(xs.begin(), xs.end());
|
|
283
|
+
int wind = 0;
|
|
284
|
+
for (size_t i = 0; i + 1 < xs.size(); ++i) {
|
|
285
|
+
wind += xs[i].second;
|
|
286
|
+
if (wind != 0) {
|
|
287
|
+
int xa = std::max(0, static_cast<int>(std::floor(xs[i].first)));
|
|
288
|
+
int xb = std::min(width - 1, static_cast<int>(std::ceil(xs[i+1].first)));
|
|
289
|
+
for (int x = xa; x <= xb; ++x) {
|
|
290
|
+
(*mask)[static_cast<size_t>(y) * width + x] = 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return mask;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 任意形状パスをクリップへ積集合で反映する (非矩形 clip() の実体)。
|
|
299
|
+
// 外接矩形 (高速リジェクト) も同時に反映し、既存 mask があれば AND を取って
|
|
300
|
+
// 新しい mask を割り当てる (save/restore 安全)。
|
|
301
|
+
inline void IntersectClipMask(ClipRect& clip, const std::vector<SubPath>& path,
|
|
302
|
+
int width, int height)
|
|
303
|
+
{
|
|
304
|
+
double minx, miny, maxx, maxy;
|
|
305
|
+
if (PathBounds(path, &minx, &miny, &maxx, &maxy)) {
|
|
306
|
+
IntersectClip(clip, std::floor(minx), std::floor(miny),
|
|
307
|
+
std::ceil(maxx), std::ceil(maxy));
|
|
308
|
+
}
|
|
309
|
+
auto next = RasterizePathMask(path, width, height);
|
|
310
|
+
if (clip.mask) {
|
|
311
|
+
const std::vector<uint8_t>& prev = *clip.mask;
|
|
312
|
+
const size_t n = std::min(next->size(), prev.size());
|
|
313
|
+
for (size_t i = 0; i < n; ++i) {
|
|
314
|
+
if (prev[i] == 0) { (*next)[i] = 0; }
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
clip.mask = next;
|
|
318
|
+
clip.mask_w = width;
|
|
319
|
+
clip.mask_h = height;
|
|
320
|
+
}
|
|
321
|
+
|
|
218
322
|
// 3次ベジェを現在サブパスへフラット化して追記する。p0 は変換済みの直前点。
|
|
219
323
|
inline void FlattenCubic(std::vector<Point>& out,
|
|
220
324
|
Point p0, Point p1, Point p2, Point p3, int steps = 16)
|
|
@@ -15,15 +15,20 @@
|
|
|
15
15
|
#include "v8/V8Util.h"
|
|
16
16
|
#include "v8/WeakHandle.h"
|
|
17
17
|
|
|
18
|
+
#include <string>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
// Media Foundation はコンソール (Game Core OS) に無いデスクトップ専用 API。
|
|
22
|
+
// コンソールでは動画デコード未対応 (末尾の «CONSOLE» スタブ)。
|
|
23
|
+
// 実機での動画対応は devkit 段階の課題。
|
|
24
|
+
#if !NEXT2D_XBOX_CONSOLE
|
|
25
|
+
|
|
18
26
|
#include <mfapi.h>
|
|
19
27
|
#include <mfidl.h>
|
|
20
28
|
#include <mfreadwrite.h>
|
|
21
29
|
#include <shlwapi.h>
|
|
22
30
|
#include <wrl/client.h>
|
|
23
31
|
|
|
24
|
-
#include <string>
|
|
25
|
-
#include <vector>
|
|
26
|
-
|
|
27
32
|
#pragma comment(lib, "mfplat.lib")
|
|
28
33
|
#pragma comment(lib, "mfreadwrite.lib")
|
|
29
34
|
#pragma comment(lib, "mfuuid.lib")
|
|
@@ -322,3 +327,52 @@ v8::Local<v8::Object> CreateVideoElement(v8::Isolate* isolate, HostContext* /*ho
|
|
|
322
327
|
}
|
|
323
328
|
|
|
324
329
|
} // namespace next2d
|
|
330
|
+
|
|
331
|
+
#else // NEXT2D_XBOX_CONSOLE
|
|
332
|
+
|
|
333
|
+
// «CONSOLE» 動画デコード未対応スタブ。
|
|
334
|
+
// createElement('video') がクラッシュしないよう API 形状だけ提供する
|
|
335
|
+
// (フレーム取得は常に false、canPlayType は空 = 非対応を正しく通知)。
|
|
336
|
+
namespace next2d {
|
|
337
|
+
|
|
338
|
+
using v8util::Str;
|
|
339
|
+
using v8util::SetMethod;
|
|
340
|
+
using v8util::SetValue;
|
|
341
|
+
|
|
342
|
+
bool GetVideoFramePixels(v8::Isolate*, v8::Local<v8::Object>,
|
|
343
|
+
const uint8_t** out_rgba, uint32_t* out_width, uint32_t* out_height)
|
|
344
|
+
{
|
|
345
|
+
*out_rgba = nullptr;
|
|
346
|
+
*out_width = 0;
|
|
347
|
+
*out_height = 0;
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
v8::Local<v8::Object> CreateVideoElement(v8::Isolate* isolate, HostContext* /*host*/)
|
|
352
|
+
{
|
|
353
|
+
v8::Local<v8::Object> self = v8::Object::New(isolate);
|
|
354
|
+
SetValue(isolate, self, "__isVideoElement", v8::Boolean::New(isolate, true));
|
|
355
|
+
SetValue(isolate, self, "tagName", Str(isolate, "VIDEO"));
|
|
356
|
+
SetValue(isolate, self, "localName", Str(isolate, "video"));
|
|
357
|
+
SetValue(isolate, self, "videoWidth", v8::Integer::New(isolate, 0));
|
|
358
|
+
SetValue(isolate, self, "videoHeight", v8::Integer::New(isolate, 0));
|
|
359
|
+
SetValue(isolate, self, "paused", v8::Boolean::New(isolate, true));
|
|
360
|
+
SetValue(isolate, self, "ended", v8::Boolean::New(isolate, false));
|
|
361
|
+
InstallEventTarget(isolate, self);
|
|
362
|
+
SetMethod(isolate, self, "load", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
363
|
+
SetMethod(isolate, self, "play", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
364
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
365
|
+
auto r = v8::Promise::Resolver::New(iso->GetCurrentContext()).ToLocalChecked();
|
|
366
|
+
r->Resolve(iso->GetCurrentContext(), v8::Undefined(iso)).Check();
|
|
367
|
+
a.GetReturnValue().Set(r->GetPromise());
|
|
368
|
+
});
|
|
369
|
+
SetMethod(isolate, self, "pause", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
370
|
+
SetMethod(isolate, self, "canPlayType", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
371
|
+
a.GetReturnValue().Set(Str(a.GetIsolate(), ""));
|
|
372
|
+
});
|
|
373
|
+
return self;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
} // namespace next2d
|
|
377
|
+
|
|
378
|
+
#endif // !NEXT2D_XBOX_CONSOLE
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
#include "gpu/DawnContext.h"
|
|
9
9
|
#include "bindings/ImageSource.h"
|
|
10
10
|
|
|
11
|
+
#include "v8/V8Util.h"
|
|
12
|
+
|
|
11
13
|
#include <cstring>
|
|
12
14
|
#include <iostream>
|
|
13
15
|
#include <map>
|
|
@@ -1211,9 +1213,13 @@ static void Device_CreateBindGroupLayout(const v8::FunctionCallbackInfo<v8::Valu
|
|
|
1211
1213
|
v8::Local<v8::Object> d = args[0].As<v8::Object>();
|
|
1212
1214
|
|
|
1213
1215
|
std::vector<wgpu::BindGroupLayoutEntry> entries;
|
|
1216
|
+
// externalTexture の nextInChain 構造体は CreateBindGroupLayout 完了まで生存が必要。
|
|
1217
|
+
// reserve でベクタ再割当を防ぎ、back() のアドレスを安定させる。
|
|
1218
|
+
std::vector<wgpu::ExternalTextureBindingLayout> ext_layouts;
|
|
1214
1219
|
v8::Local<v8::Value> ev = Prop(isolate, d, "entries");
|
|
1215
1220
|
if (ev->IsArray()) {
|
|
1216
1221
|
auto arr = ev.As<v8::Array>();
|
|
1222
|
+
ext_layouts.reserve(arr->Length());
|
|
1217
1223
|
for (uint32_t i = 0; i < arr->Length(); ++i) {
|
|
1218
1224
|
v8::Local<v8::Value> v;
|
|
1219
1225
|
if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
|
|
@@ -1237,8 +1243,21 @@ static void Device_CreateBindGroupLayout(const v8::FunctionCallbackInfo<v8::Valu
|
|
|
1237
1243
|
? ToTextureViewDimension(webgpu::Str(isolate, t, "viewDimension"))
|
|
1238
1244
|
: wgpu::TextureViewDimension::e2D;
|
|
1239
1245
|
entry.texture.multisampled = Bool(isolate, t, "multisampled", false);
|
|
1246
|
+
} else if (HasProp(isolate, o, "storageTexture")) {
|
|
1247
|
+
auto st = Prop(isolate, o, "storageTexture").As<v8::Object>();
|
|
1248
|
+
entry.storageTexture.access = HasProp(isolate, st, "access")
|
|
1249
|
+
? ToStorageTextureAccess(webgpu::Str(isolate, st, "access"))
|
|
1250
|
+
: wgpu::StorageTextureAccess::WriteOnly;
|
|
1251
|
+
entry.storageTexture.format = ToTextureFormat(webgpu::Str(isolate, st, "format"));
|
|
1252
|
+
entry.storageTexture.viewDimension = HasProp(isolate, st, "viewDimension")
|
|
1253
|
+
? ToTextureViewDimension(webgpu::Str(isolate, st, "viewDimension"))
|
|
1254
|
+
: wgpu::TextureViewDimension::e2D;
|
|
1255
|
+
} else if (HasProp(isolate, o, "externalTexture")) {
|
|
1256
|
+
// GPUExternalTextureBindingLayout は空 (メンバ無し)。sType は
|
|
1257
|
+
// Dawn の C++ ラッパが既定で設定するため emplace してチェーンするだけでよい。
|
|
1258
|
+
ext_layouts.emplace_back();
|
|
1259
|
+
entry.nextInChain = &ext_layouts.back();
|
|
1240
1260
|
}
|
|
1241
|
-
// «EXTEND» storageTexture / externalTexture は必要に応じて追加
|
|
1242
1261
|
entries.push_back(entry);
|
|
1243
1262
|
}
|
|
1244
1263
|
}
|
|
@@ -1347,6 +1366,11 @@ struct RenderPipelineScratch {
|
|
|
1347
1366
|
// ConstantEntry.key が指すため CreateRenderPipeline 完了まで生存させる。
|
|
1348
1367
|
std::vector<std::string> vs_const_keys, fs_const_keys;
|
|
1349
1368
|
std::vector<wgpu::ConstantEntry> vs_constants, fs_constants;
|
|
1369
|
+
// desc.depthStencil / desc.fragment はこれらのアドレスを指す。ディスクリプタ構築を
|
|
1370
|
+
// ヘルパに切り出したため、ローカルだとヘルパ return でダングリング化する。scratch に
|
|
1371
|
+
// 保持して CreateRenderPipeline[Async] が同期コピーするまで生存させる。
|
|
1372
|
+
wgpu::DepthStencilState depth = {};
|
|
1373
|
+
wgpu::FragmentState fragment = {};
|
|
1350
1374
|
};
|
|
1351
1375
|
|
|
1352
1376
|
// { "KEY": number|bool, ... } を wgpu::ConstantEntry 配列へ変換する。
|
|
@@ -1472,7 +1496,8 @@ static void FillRenderPipelineDescriptor(v8::Isolate* isolate, v8::Local<v8::Obj
|
|
|
1472
1496
|
}
|
|
1473
1497
|
|
|
1474
1498
|
// depthStencil (マスク処理は stencil8 + stencilFront/Back を使う)
|
|
1475
|
-
|
|
1499
|
+
// scratch.depth を参照 (アドレスを desc.depthStencil に格納するため寿命を延ばす)。
|
|
1500
|
+
wgpu::DepthStencilState& depth = scratch.depth;
|
|
1476
1501
|
v8::Local<v8::Value> dsv = Prop(isolate, d, "depthStencil");
|
|
1477
1502
|
if (dsv->IsObject()) {
|
|
1478
1503
|
auto ds = dsv.As<v8::Object>();
|
|
@@ -1532,7 +1557,8 @@ static void FillRenderPipelineDescriptor(v8::Isolate* isolate, v8::Local<v8::Obj
|
|
|
1532
1557
|
}
|
|
1533
1558
|
|
|
1534
1559
|
// fragment
|
|
1535
|
-
|
|
1560
|
+
// scratch.fragment を参照 (アドレスを desc.fragment に格納するため寿命を延ばす)。
|
|
1561
|
+
wgpu::FragmentState& fragment = scratch.fragment;
|
|
1536
1562
|
v8::Local<v8::Value> fv = Prop(isolate, d, "fragment");
|
|
1537
1563
|
if (fv->IsObject()) {
|
|
1538
1564
|
auto f = fv.As<v8::Object>();
|
|
@@ -61,12 +61,15 @@ inline v8::Local<v8::ObjectTemplate> HandleTemplate(v8::Isolate* isolate)
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// --- JS オブジェクト読み取りヘルパー ------------------------------------
|
|
64
|
+
// プロパティ名はキャッシュ済み internalized string (v8util::PropName) を使う。
|
|
65
|
+
// ここは全ディスクリプタ解析の合流点で毎フレーム数千回通るため、名前 v8::String の
|
|
66
|
+
// 毎回生成 (ヒープ確保 + ハッシュ) を排除する効果が大きい。
|
|
64
67
|
inline v8::Local<v8::Value> Prop(v8::Isolate* isolate, v8::Local<v8::Object> obj,
|
|
65
68
|
const char* key)
|
|
66
69
|
{
|
|
67
70
|
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
68
71
|
v8::Local<v8::Value> v;
|
|
69
|
-
if (obj->Get(ctx, v8util::
|
|
72
|
+
if (obj->Get(ctx, v8util::PropName(isolate, key)).ToLocal(&v)) {
|
|
70
73
|
return v;
|
|
71
74
|
}
|
|
72
75
|
return v8::Undefined(isolate);
|
|
@@ -17,22 +17,54 @@ inline wgpu::TextureFormat ToTextureFormat(std::string_view s)
|
|
|
17
17
|
if (s == "bgra8unorm") return F::BGRA8Unorm;
|
|
18
18
|
if (s == "bgra8unorm-srgb") return F::BGRA8UnormSrgb;
|
|
19
19
|
if (s == "rgba8snorm") return F::RGBA8Snorm;
|
|
20
|
+
if (s == "rgba8uint") return F::RGBA8Uint;
|
|
21
|
+
if (s == "rgba8sint") return F::RGBA8Sint;
|
|
20
22
|
if (s == "r8unorm") return F::R8Unorm;
|
|
23
|
+
if (s == "r8snorm") return F::R8Snorm;
|
|
24
|
+
if (s == "r8uint") return F::R8Uint;
|
|
25
|
+
if (s == "r8sint") return F::R8Sint;
|
|
21
26
|
if (s == "rg8unorm") return F::RG8Unorm;
|
|
27
|
+
if (s == "rg8snorm") return F::RG8Snorm;
|
|
28
|
+
if (s == "rg8uint") return F::RG8Uint;
|
|
29
|
+
if (s == "rg8sint") return F::RG8Sint;
|
|
30
|
+
if (s == "r16uint") return F::R16Uint;
|
|
31
|
+
if (s == "r16sint") return F::R16Sint;
|
|
22
32
|
if (s == "r16float") return F::R16Float;
|
|
33
|
+
if (s == "rg16uint") return F::RG16Uint;
|
|
34
|
+
if (s == "rg16sint") return F::RG16Sint;
|
|
23
35
|
if (s == "rg16float") return F::RG16Float;
|
|
36
|
+
if (s == "rgba16uint") return F::RGBA16Uint;
|
|
37
|
+
if (s == "rgba16sint") return F::RGBA16Sint;
|
|
24
38
|
if (s == "rgba16float") return F::RGBA16Float;
|
|
39
|
+
if (s == "r32uint") return F::R32Uint;
|
|
40
|
+
if (s == "r32sint") return F::R32Sint;
|
|
25
41
|
if (s == "r32float") return F::R32Float;
|
|
42
|
+
if (s == "rg32uint") return F::RG32Uint;
|
|
43
|
+
if (s == "rg32sint") return F::RG32Sint;
|
|
26
44
|
if (s == "rg32float") return F::RG32Float;
|
|
45
|
+
if (s == "rgba32uint") return F::RGBA32Uint;
|
|
46
|
+
if (s == "rgba32sint") return F::RGBA32Sint;
|
|
27
47
|
if (s == "rgba32float") return F::RGBA32Float;
|
|
48
|
+
if (s == "rgb10a2unorm") return F::RGB10A2Unorm;
|
|
49
|
+
if (s == "rg11b10ufloat") return F::RG11B10Ufloat;
|
|
28
50
|
if (s == "depth16unorm") return F::Depth16Unorm;
|
|
29
51
|
if (s == "depth24plus") return F::Depth24Plus;
|
|
30
52
|
if (s == "depth24plus-stencil8") return F::Depth24PlusStencil8;
|
|
31
53
|
if (s == "depth32float") return F::Depth32Float;
|
|
54
|
+
if (s == "depth32float-stencil8") return F::Depth32FloatStencil8;
|
|
32
55
|
if (s == "stencil8") return F::Stencil8;
|
|
33
56
|
return F::RGBA8Unorm;
|
|
34
57
|
}
|
|
35
58
|
|
|
59
|
+
inline wgpu::StorageTextureAccess ToStorageTextureAccess(std::string_view s)
|
|
60
|
+
{
|
|
61
|
+
using A = wgpu::StorageTextureAccess;
|
|
62
|
+
if (s == "write-only") return A::WriteOnly;
|
|
63
|
+
if (s == "read-only") return A::ReadOnly;
|
|
64
|
+
if (s == "read-write") return A::ReadWrite;
|
|
65
|
+
return A::WriteOnly;
|
|
66
|
+
}
|
|
67
|
+
|
|
36
68
|
inline wgpu::VertexFormat ToVertexFormat(std::string_view s)
|
|
37
69
|
{
|
|
38
70
|
using V = wgpu::VertexFormat;
|
|
@@ -1,22 +1,160 @@
|
|
|
1
1
|
#include "DawnContext.h"
|
|
2
2
|
|
|
3
3
|
#include <Windows.h>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <cstdio>
|
|
7
|
+
#include <cstdlib>
|
|
8
|
+
#include <filesystem>
|
|
9
|
+
#include <fstream>
|
|
4
10
|
#include <iostream>
|
|
11
|
+
#include <string>
|
|
5
12
|
|
|
6
13
|
namespace next2d {
|
|
7
14
|
|
|
8
15
|
namespace {
|
|
9
16
|
|
|
17
|
+
// GUI 実行では stderr が見えないため、GPU の致命的エラー/検証エラーを
|
|
18
|
+
// next2d-error.log にも残す。描画が黒くなる/表示されない等の原因(検証エラー・
|
|
19
|
+
// device lost・リソース枯渇)を掴む唯一の手段。氾濫防止に総数を制限する。
|
|
20
|
+
void AppendGpuLog(const std::string& line)
|
|
21
|
+
{
|
|
22
|
+
static int count = 0;
|
|
23
|
+
if (count >= 60) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
++count;
|
|
27
|
+
std::ofstream ofs("next2d-error.log", std::ios::app);
|
|
28
|
+
if (ofs) {
|
|
29
|
+
ofs << line << std::endl;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
10
33
|
void HandleDeviceLost(const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message)
|
|
11
34
|
{
|
|
12
|
-
std::
|
|
13
|
-
|
|
35
|
+
const std::string line = "[Dawn] Device lost (" + std::to_string(static_cast<int>(reason))
|
|
36
|
+
+ "): " + std::string(message.data, message.length);
|
|
37
|
+
std::cerr << line << std::endl;
|
|
38
|
+
AppendGpuLog(line);
|
|
14
39
|
}
|
|
15
40
|
|
|
16
41
|
void HandleUncapturedError(const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message)
|
|
17
42
|
{
|
|
18
|
-
std::
|
|
19
|
-
|
|
43
|
+
const std::string line = "[Dawn] Uncaptured error (" + std::to_string(static_cast<int>(type))
|
|
44
|
+
+ "): " + std::string(message.data, message.length);
|
|
45
|
+
std::cerr << line << std::endl;
|
|
46
|
+
AppendGpuLog(line);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- GPU Blob キャッシュ (シェーダ/パイプラインのコンパイル結果) -------------
|
|
50
|
+
// D3D12 の HLSL コンパイル (FXC) はパイプライン初回生成時に走り、起動直後や
|
|
51
|
+
// 新しいエフェクト初出時のフレームヒッチの原因になる。Dawn の BlobCache を
|
|
52
|
+
// %LOCALAPPDATA%\Next2D\gpucache に永続化し、2 回目以降の起動で再利用する
|
|
53
|
+
// (V8 バイトコードキャッシュの GPU 版)。
|
|
54
|
+
// 呼び出し規約 (dawn/native/BlobCache.cpp LoadInternal で検証済み):
|
|
55
|
+
// load は 2 段階 — まず value=nullptr でサイズ問い合わせ (戻り値 0 = miss)、
|
|
56
|
+
// 次に確保済みバッファへの読み込み (戻り値がサイズと不一致なら miss 扱い)。
|
|
57
|
+
// キーには Dawn がアダプタ/ドライバ情報を織り込むため、環境が変われば自然に
|
|
58
|
+
// 別エントリになる。あらゆる失敗は 0 / 何もしない (graceful degrade)。
|
|
59
|
+
|
|
60
|
+
uint64_t Fnv1a(const void* data, size_t size, uint64_t hash = 1469598103934665603ull)
|
|
61
|
+
{
|
|
62
|
+
const auto* p = static_cast<const unsigned char*>(data);
|
|
63
|
+
for (size_t i = 0; i < size; ++i) {
|
|
64
|
+
hash ^= p[i];
|
|
65
|
+
hash *= 1099511628211ull;
|
|
66
|
+
}
|
|
67
|
+
return hash;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const std::filesystem::path& GpuCacheDir()
|
|
71
|
+
{
|
|
72
|
+
static const std::filesystem::path dir = []() -> std::filesystem::path {
|
|
73
|
+
const char* base = std::getenv("LOCALAPPDATA");
|
|
74
|
+
if (!base || !*base) {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
std::error_code ec;
|
|
78
|
+
std::filesystem::path d = std::filesystem::path(base) / "Next2D" / "gpucache";
|
|
79
|
+
std::filesystem::create_directories(d, ec);
|
|
80
|
+
if (ec) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
// ドライバ/Dawn 更新でキーが変わると旧 blob は孤児になる。
|
|
84
|
+
// 30 日アクセスの無い .blob を掃除する (load ヒット時に touch する LRU)。
|
|
85
|
+
const auto now = std::filesystem::file_time_type::clock::now();
|
|
86
|
+
std::error_code iter_ec;
|
|
87
|
+
for (const auto& entry : std::filesystem::directory_iterator(d, iter_ec)) {
|
|
88
|
+
std::error_code e2;
|
|
89
|
+
if (entry.path().extension() != ".blob") {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const auto t = std::filesystem::last_write_time(entry.path(), e2);
|
|
93
|
+
if (!e2 && now - t > std::chrono::hours(24 * 30)) {
|
|
94
|
+
std::filesystem::remove(entry.path(), e2);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return d;
|
|
98
|
+
}();
|
|
99
|
+
return dir;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
std::filesystem::path GpuCacheFileFor(const void* key, size_t key_size)
|
|
103
|
+
{
|
|
104
|
+
const std::filesystem::path& dir = GpuCacheDir();
|
|
105
|
+
if (dir.empty()) {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
char buf[17] = {};
|
|
109
|
+
std::snprintf(buf, sizeof(buf), "%016llx",
|
|
110
|
+
static_cast<unsigned long long>(Fnv1a(key, key_size)));
|
|
111
|
+
return dir / (std::string(buf) + ".blob");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
size_t LoadGpuCacheData(const void* key, size_t key_size,
|
|
115
|
+
void* value, size_t value_size, void*)
|
|
116
|
+
{
|
|
117
|
+
const std::filesystem::path file = GpuCacheFileFor(key, key_size);
|
|
118
|
+
if (file.empty()) {
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
std::error_code ec;
|
|
122
|
+
const auto size = std::filesystem::file_size(file, ec);
|
|
123
|
+
if (ec || size == 0) {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
if (value == nullptr) {
|
|
127
|
+
// 1 段階目: サイズ問い合わせ
|
|
128
|
+
return static_cast<size_t>(size);
|
|
129
|
+
}
|
|
130
|
+
if (value_size < size) {
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
std::ifstream ifs(file, std::ios::binary);
|
|
134
|
+
if (!ifs || !ifs.read(static_cast<char*>(value), static_cast<std::streamsize>(size))) {
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
// 使用時に touch して 30 日掃除を LRU として機能させる
|
|
138
|
+
std::filesystem::last_write_time(
|
|
139
|
+
file, std::filesystem::file_time_type::clock::now(), ec);
|
|
140
|
+
return static_cast<size_t>(size);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
void StoreGpuCacheData(const void* key, size_t key_size,
|
|
144
|
+
const void* value, size_t value_size, void*)
|
|
145
|
+
{
|
|
146
|
+
if (!value || value_size == 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const std::filesystem::path file = GpuCacheFileFor(key, key_size);
|
|
150
|
+
if (file.empty()) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
std::ofstream ofs(file, std::ios::binary | std::ios::trunc);
|
|
154
|
+
if (ofs) {
|
|
155
|
+
ofs.write(static_cast<const char*>(value),
|
|
156
|
+
static_cast<std::streamsize>(value_size));
|
|
157
|
+
}
|
|
20
158
|
}
|
|
21
159
|
|
|
22
160
|
} // namespace
|
|
@@ -95,6 +233,28 @@ bool DawnContext::AcquireDevice()
|
|
|
95
233
|
device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowProcessEvents, HandleDeviceLost);
|
|
96
234
|
device_desc.SetUncapturedErrorCallback(HandleUncapturedError);
|
|
97
235
|
|
|
236
|
+
// GPU Blob キャッシュを接続する (シェーダコンパイル結果の永続化)。
|
|
237
|
+
// 関数ポインタは Dawn が device 生成時に BlobCache へコピーするため、
|
|
238
|
+
// この記述子自体は RequestDevice 完了までの生存で足りる。
|
|
239
|
+
wgpu::DawnCacheDeviceDescriptor cache_desc = {};
|
|
240
|
+
cache_desc.loadDataFunction = LoadGpuCacheData;
|
|
241
|
+
cache_desc.storeDataFunction = StoreGpuCacheData;
|
|
242
|
+
device_desc.nextInChain = &cache_desc;
|
|
243
|
+
|
|
244
|
+
#ifdef NDEBUG
|
|
245
|
+
// Release では Dawn の API 検証 (skip_validation) と robustness (OOB クランプ) を
|
|
246
|
+
// 無効化する。両方とも全描画コマンドのエンコードで毎フレーム CPU を消費する
|
|
247
|
+
// 安全網で、検証済みタイトルの出荷ビルドでは Chrome も同様に外す定番最適化。
|
|
248
|
+
// Debug ビルドでは有効のままにして検証エラーを検出できるようにする
|
|
249
|
+
// (エラーは HandleUncapturedError -> next2d-error.log へ出る)。
|
|
250
|
+
// toggle 名は Dawn 本体 src/dawn/native/Toggles.cpp 定義 (いずれも ToggleStage::Device)。
|
|
251
|
+
static const char* kReleaseToggles[] = {"skip_validation", "disable_robustness"};
|
|
252
|
+
wgpu::DawnTogglesDescriptor device_toggles = {};
|
|
253
|
+
device_toggles.enabledToggles = kReleaseToggles;
|
|
254
|
+
device_toggles.enabledToggleCount = 2;
|
|
255
|
+
cache_desc.nextInChain = &device_toggles; // device -> cache -> toggles
|
|
256
|
+
#endif
|
|
257
|
+
|
|
98
258
|
wgpu::Future device_future = adapter_.RequestDevice(
|
|
99
259
|
&device_desc,
|
|
100
260
|
wgpu::CallbackMode::AllowProcessEvents,
|
|
@@ -137,11 +297,10 @@ void DawnContext::Configure(uint32_t width, uint32_t height)
|
|
|
137
297
|
wgpu::SurfaceConfiguration config = {};
|
|
138
298
|
config.device = device_;
|
|
139
299
|
config.format = format_;
|
|
300
|
+
// RenderAttachment のみ。CopySrc を足すと swapchain の flip 提示最適化が無効化され
|
|
301
|
+
// 提示ごとにコピーが挟まって重くなるため、描画確認が済んだ今は付けない
|
|
302
|
+
// (以前は DebugProbeSurface の黒画面調査用に付けていた)。
|
|
140
303
|
config.usage = wgpu::TextureUsage::RenderAttachment;
|
|
141
|
-
if (can_copy_src_) {
|
|
142
|
-
// 黒画面デバッグ用の surface 読み戻し (DebugProbeSurface) を可能にする
|
|
143
|
-
config.usage |= wgpu::TextureUsage::CopySrc;
|
|
144
|
-
}
|
|
145
304
|
config.width = width_;
|
|
146
305
|
config.height = height_;
|
|
147
306
|
config.presentMode = wgpu::PresentMode::Fifo;
|
|
@@ -231,17 +390,9 @@ void DawnContext::Present()
|
|
|
231
390
|
// 取得なしで Present すると Dawn の検証エラーになる (描画しないフレームは提示しない)。
|
|
232
391
|
if (surface_ && configured_ && frame_texture_acquired_) {
|
|
233
392
|
++present_count_;
|
|
234
|
-
if (present_count_ <= 3 || present_count_ == 60 || present_count_ % 300 == 0) {
|
|
235
|
-
DebugProbeSurface();
|
|
236
|
-
}
|
|
237
393
|
surface_.Present();
|
|
238
394
|
} else {
|
|
239
395
|
++present_skipped_;
|
|
240
|
-
if (present_skipped_ <= 3 || present_skipped_ % 300 == 0) {
|
|
241
|
-
std::cerr << "[GPU] present skipped #" << present_skipped_
|
|
242
|
-
<< " (configured=" << configured_
|
|
243
|
-
<< " acquired=" << frame_texture_acquired_ << ")" << std::endl;
|
|
244
|
-
}
|
|
245
396
|
}
|
|
246
397
|
frame_texture_acquired_ = false;
|
|
247
398
|
current_texture_ = nullptr;
|