@next2d/builder 0.1.5 → 0.1.7

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 (54) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +315 -3
  3. package/package.json +10 -8
  4. package/templates/xbox/CMakeLists.txt +124 -0
  5. package/templates/xbox/MicrosoftGame.config +36 -0
  6. package/templates/xbox/README.md +372 -0
  7. package/templates/xbox/cmake/FetchDawn.cmake +42 -0
  8. package/templates/xbox/cmake/FindV8.cmake +68 -0
  9. package/templates/xbox/js/bootstrap.js +143 -0
  10. package/templates/xbox/js/selftest.js +515 -0
  11. package/templates/xbox/src/AssetLoader.cpp +106 -0
  12. package/templates/xbox/src/AssetLoader.h +35 -0
  13. package/templates/xbox/src/EventLoop.cpp +162 -0
  14. package/templates/xbox/src/EventLoop.h +73 -0
  15. package/templates/xbox/src/HostContext.h +58 -0
  16. package/templates/xbox/src/bindings/Audio.cpp +320 -0
  17. package/templates/xbox/src/bindings/Bindings.h +75 -0
  18. package/templates/xbox/src/bindings/Canvas.cpp +171 -0
  19. package/templates/xbox/src/bindings/Canvas2D.cpp +462 -0
  20. package/templates/xbox/src/bindings/Console.cpp +52 -0
  21. package/templates/xbox/src/bindings/DomShims.cpp +221 -0
  22. package/templates/xbox/src/bindings/EventTarget.cpp +127 -0
  23. package/templates/xbox/src/bindings/EventTarget.h +17 -0
  24. package/templates/xbox/src/bindings/Fetch.cpp +149 -0
  25. package/templates/xbox/src/bindings/Gamepad.cpp +79 -0
  26. package/templates/xbox/src/bindings/Image.cpp +160 -0
  27. package/templates/xbox/src/bindings/ImageSource.h +26 -0
  28. package/templates/xbox/src/bindings/Network.cpp +303 -0
  29. package/templates/xbox/src/bindings/RasterCore.h +265 -0
  30. package/templates/xbox/src/bindings/Timers.cpp +87 -0
  31. package/templates/xbox/src/bindings/Video.cpp +235 -0
  32. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +1318 -0
  33. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +119 -0
  34. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +245 -0
  35. package/templates/xbox/src/gpu/DawnContext.cpp +170 -0
  36. package/templates/xbox/src/gpu/DawnContext.h +64 -0
  37. package/templates/xbox/src/main.cpp +368 -0
  38. package/templates/xbox/src/platform/AudioEngine.cpp +184 -0
  39. package/templates/xbox/src/platform/AudioEngine.h +65 -0
  40. package/templates/xbox/src/platform/GamepadManager.cpp +83 -0
  41. package/templates/xbox/src/platform/GamepadManager.h +40 -0
  42. package/templates/xbox/src/platform/ImageTypes.h +17 -0
  43. package/templates/xbox/src/platform/TextRasterizer.cpp +183 -0
  44. package/templates/xbox/src/platform/TextRasterizer.h +35 -0
  45. package/templates/xbox/src/platform/WicDecoder.cpp +63 -0
  46. package/templates/xbox/src/platform/WicDecoder.h +16 -0
  47. package/templates/xbox/src/v8/V8Runtime.cpp +285 -0
  48. package/templates/xbox/src/v8/V8Runtime.h +68 -0
  49. package/templates/xbox/src/v8/V8Util.h +56 -0
  50. package/templates/xbox/src/worker/WorkerRuntime.cpp +453 -0
  51. package/templates/xbox/src/worker/WorkerRuntime.h +108 -0
  52. package/templates/xbox/tests/check_local.sh +56 -0
  53. package/templates/xbox/tests/raster_test.cpp +279 -0
  54. package/templates/xbox/tests/windows_platform_test.cpp +187 -0
@@ -0,0 +1,1318 @@
1
+ // WebGPU (navigator.gpu) を Dawn へ橋渡しするバインディング本体。
2
+ // WebGPUCommon.h の «実装範囲» コメントを参照。
3
+ #include "bindings/Bindings.h"
4
+ #include "bindings/webgpu/WebGPUCommon.h"
5
+ #include "bindings/webgpu/WebGPUEnums.h"
6
+
7
+ #include "HostContext.h"
8
+ #include "gpu/DawnContext.h"
9
+ #include "bindings/ImageSource.h"
10
+
11
+ #include <cstring>
12
+ #include <map>
13
+ #include <vector>
14
+
15
+ namespace next2d {
16
+ namespace webgpu {
17
+
18
+ using v8util::Str;
19
+ using v8util::SetMethod;
20
+ using v8util::SetValue;
21
+
22
+ // 各 wgpu 型に対応する ObjectTemplate 群 (単一 isolate 前提)。
23
+ struct Templates {
24
+ v8::Global<v8::ObjectTemplate> adapter, device, queue, buffer, texture,
25
+ textureView, sampler, shaderModule, bindGroupLayout, bindGroup,
26
+ pipelineLayout, renderPipeline, commandEncoder, renderPass,
27
+ commandBuffer, canvasContext;
28
+ };
29
+
30
+ static std::map<v8::Isolate*, Templates> g_templates;
31
+
32
+ static Templates& Tmpl(v8::Isolate* isolate)
33
+ {
34
+ return g_templates[isolate];
35
+ }
36
+
37
+ template <typename T>
38
+ static v8::Local<v8::Object> WrapWith(v8::Isolate* isolate,
39
+ v8::Global<v8::ObjectTemplate>& g, T handle)
40
+ {
41
+ return Wrap<T>(isolate, g.Get(isolate), std::move(handle));
42
+ }
43
+
44
+ // メソッド付与済みのラップ (前方宣言。定義は device セクション付近)
45
+ static v8::Local<v8::Object> WrapBuffer(v8::Isolate*, wgpu::Buffer);
46
+ static v8::Local<v8::Object> WrapTexture(v8::Isolate*, wgpu::Texture);
47
+ static v8::Local<v8::Object> WrapTextureView(v8::Isolate*, wgpu::TextureView);
48
+ static v8::Local<v8::Object> WrapEncoder(v8::Isolate*, wgpu::CommandEncoder);
49
+ static v8::Local<v8::Object> WrapPipeline(v8::Isolate*, wgpu::RenderPipeline);
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // GPUBuffer
53
+ // ---------------------------------------------------------------------------
54
+ static void Buffer_GetMappedRange(const v8::FunctionCallbackInfo<v8::Value>& args)
55
+ {
56
+ v8::Isolate* isolate = args.GetIsolate();
57
+ wgpu::Buffer& buffer = Unwrap<wgpu::Buffer>(args.This());
58
+
59
+ uint64_t offset = args.Length() > 0 && args[0]->IsNumber()
60
+ ? static_cast<uint64_t>(args[0].As<v8::Number>()->Value()) : 0;
61
+ uint64_t size = args.Length() > 1 && args[1]->IsNumber()
62
+ ? static_cast<uint64_t>(args[1].As<v8::Number>()->Value())
63
+ : (buffer.GetSize() - offset);
64
+
65
+ void* ptr = buffer.GetMappedRange(offset, static_cast<size_t>(size));
66
+ if (!ptr) {
67
+ args.GetReturnValue().Set(v8::ArrayBuffer::New(isolate, 0));
68
+ return;
69
+ }
70
+ // マップ領域を指す非所有 ArrayBuffer (unmap まで有効)
71
+ std::unique_ptr<v8::BackingStore> store = v8::ArrayBuffer::NewBackingStore(
72
+ ptr, static_cast<size_t>(size),
73
+ [](void*, size_t, void*) {}, nullptr);
74
+ args.GetReturnValue().Set(v8::ArrayBuffer::New(isolate, std::move(store)));
75
+ }
76
+
77
+ static void Buffer_Unmap(const v8::FunctionCallbackInfo<v8::Value>& args)
78
+ {
79
+ Unwrap<wgpu::Buffer>(args.This()).Unmap();
80
+ }
81
+
82
+ static void Buffer_Destroy(const v8::FunctionCallbackInfo<v8::Value>& args)
83
+ {
84
+ Unwrap<wgpu::Buffer>(args.This()).Destroy();
85
+ }
86
+
87
+ static void Buffer_MapAsync(const v8::FunctionCallbackInfo<v8::Value>& args)
88
+ {
89
+ v8::Isolate* isolate = args.GetIsolate();
90
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
91
+ wgpu::Buffer& buffer = Unwrap<wgpu::Buffer>(args.This());
92
+
93
+ wgpu::MapMode mode = static_cast<wgpu::MapMode>(
94
+ args.Length() > 0 ? static_cast<uint32_t>(args[0].As<v8::Number>()->Value()) : 0);
95
+ uint64_t offset = args.Length() > 1 && args[1]->IsNumber()
96
+ ? static_cast<uint64_t>(args[1].As<v8::Number>()->Value()) : 0;
97
+ uint64_t size = args.Length() > 2 && args[2]->IsNumber()
98
+ ? static_cast<uint64_t>(args[2].As<v8::Number>()->Value())
99
+ : (buffer.GetSize() - offset);
100
+
101
+ auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
102
+ auto* persistent = new v8::Global<v8::Promise::Resolver>(isolate, resolver);
103
+ args.GetReturnValue().Set(resolver->GetPromise());
104
+
105
+ buffer.MapAsync(mode, offset, static_cast<size_t>(size),
106
+ wgpu::CallbackMode::AllowProcessEvents,
107
+ [isolate, persistent](wgpu::MapAsyncStatus status, wgpu::StringView) {
108
+ v8::HandleScope scope(isolate);
109
+ v8::Local<v8::Context> c = isolate->GetCurrentContext();
110
+ auto r = persistent->Get(isolate);
111
+ if (status == wgpu::MapAsyncStatus::Success) {
112
+ r->Resolve(c, v8::Undefined(isolate)).Check();
113
+ } else {
114
+ r->Reject(c, v8::Exception::Error(v8util::Str(isolate, "mapAsync failed"))).Check();
115
+ }
116
+ persistent->Reset();
117
+ delete persistent;
118
+ });
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // GPUTexture
123
+ // ---------------------------------------------------------------------------
124
+ static void Texture_CreateView(const v8::FunctionCallbackInfo<v8::Value>& args)
125
+ {
126
+ v8::Isolate* isolate = args.GetIsolate();
127
+ wgpu::Texture& texture = Unwrap<wgpu::Texture>(args.This());
128
+
129
+ wgpu::TextureViewDescriptor desc = {};
130
+ if (args.Length() > 0 && args[0]->IsObject()) {
131
+ v8::Local<v8::Object> o = args[0].As<v8::Object>();
132
+ if (HasProp(isolate, o, "format")) {
133
+ desc.format = ToTextureFormat(webgpu::Str(isolate, o, "format"));
134
+ }
135
+ if (HasProp(isolate, o, "dimension")) {
136
+ desc.dimension = ToTextureViewDimension(webgpu::Str(isolate, o, "dimension"));
137
+ }
138
+ if (HasProp(isolate, o, "baseMipLevel")) desc.baseMipLevel = U32(isolate, o, "baseMipLevel");
139
+ if (HasProp(isolate, o, "mipLevelCount")) desc.mipLevelCount = U32(isolate, o, "mipLevelCount");
140
+ if (HasProp(isolate, o, "baseArrayLayer")) desc.baseArrayLayer = U32(isolate, o, "baseArrayLayer");
141
+ if (HasProp(isolate, o, "arrayLayerCount")) desc.arrayLayerCount = U32(isolate, o, "arrayLayerCount");
142
+ }
143
+ args.GetReturnValue().Set(WrapTextureView(isolate, texture.CreateView(&desc)));
144
+ }
145
+
146
+ static void Texture_Destroy(const v8::FunctionCallbackInfo<v8::Value>& args)
147
+ {
148
+ Unwrap<wgpu::Texture>(args.This()).Destroy();
149
+ }
150
+
151
+ // ---------------------------------------------------------------------------
152
+ // GPUQueue
153
+ // ---------------------------------------------------------------------------
154
+ static bool CopyBytesFrom(v8::Local<v8::Value> value, const void** data, size_t* length)
155
+ {
156
+ if (value->IsArrayBuffer()) {
157
+ auto ab = value.As<v8::ArrayBuffer>();
158
+ auto store = ab->GetBackingStore();
159
+ *data = store->Data();
160
+ *length = store->ByteLength();
161
+ return true;
162
+ }
163
+ if (value->IsArrayBufferView()) {
164
+ auto view = value.As<v8::ArrayBufferView>();
165
+ auto ab = view->Buffer();
166
+ auto store = ab->GetBackingStore();
167
+ *data = static_cast<uint8_t*>(store->Data()) + view->ByteOffset();
168
+ *length = view->ByteLength();
169
+ return true;
170
+ }
171
+ return false;
172
+ }
173
+
174
+ static void Queue_WriteBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
175
+ {
176
+ v8::Isolate* isolate = args.GetIsolate();
177
+ wgpu::Queue& queue = Unwrap<wgpu::Queue>(args.This());
178
+
179
+ wgpu::Buffer& buffer = Unwrap<wgpu::Buffer>(args[0].As<v8::Object>());
180
+ uint64_t buffer_offset = static_cast<uint64_t>(args[1].As<v8::Number>()->Value());
181
+
182
+ const void* data = nullptr;
183
+ size_t length = 0;
184
+ if (!CopyBytesFrom(args[2], &data, &length)) {
185
+ v8util::ThrowTypeError(isolate, "writeBuffer: data must be ArrayBuffer(View)");
186
+ return;
187
+ }
188
+ // 任意の dataOffset/size (要素数ではなくバイト) — 簡略化のため未対応時は全体
189
+ uint64_t data_offset = args.Length() > 3 && args[3]->IsNumber()
190
+ ? static_cast<uint64_t>(args[3].As<v8::Number>()->Value()) : 0;
191
+ size_t write_size = args.Length() > 4 && args[4]->IsNumber()
192
+ ? static_cast<size_t>(args[4].As<v8::Number>()->Value())
193
+ : (length - data_offset);
194
+
195
+ queue.WriteBuffer(buffer, buffer_offset,
196
+ static_cast<const uint8_t*>(data) + data_offset, write_size);
197
+ }
198
+
199
+ static wgpu::Extent3D ParseExtent(v8::Isolate* isolate, v8::Local<v8::Value> value)
200
+ {
201
+ wgpu::Extent3D extent = {1, 1, 1};
202
+ if (value->IsArray()) {
203
+ auto arr = value.As<v8::Array>();
204
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
205
+ auto get = [&](uint32_t i, uint32_t fb) -> uint32_t {
206
+ v8::Local<v8::Value> v;
207
+ return (i < arr->Length() && arr->Get(ctx, i).ToLocal(&v) && v->IsNumber())
208
+ ? static_cast<uint32_t>(v.As<v8::Number>()->Value()) : fb;
209
+ };
210
+ extent.width = get(0, 1);
211
+ extent.height = get(1, 1);
212
+ extent.depthOrArrayLayers = get(2, 1);
213
+ } else if (value->IsObject()) {
214
+ auto o = value.As<v8::Object>();
215
+ extent.width = U32(isolate, o, "width", 1);
216
+ extent.height = U32(isolate, o, "height", 1);
217
+ extent.depthOrArrayLayers = U32(isolate, o, "depthOrArrayLayers", 1);
218
+ }
219
+ return extent;
220
+ }
221
+
222
+ static void Queue_WriteTexture(const v8::FunctionCallbackInfo<v8::Value>& args)
223
+ {
224
+ v8::Isolate* isolate = args.GetIsolate();
225
+ wgpu::Queue& queue = Unwrap<wgpu::Queue>(args.This());
226
+
227
+ // destination: { texture, mipLevel?, origin? }
228
+ v8::Local<v8::Object> dest = args[0].As<v8::Object>();
229
+ wgpu::TexelCopyTextureInfo copy = {};
230
+ copy.texture = Unwrap<wgpu::Texture>(Prop(isolate, dest, "texture").As<v8::Object>());
231
+ copy.mipLevel = U32(isolate, dest, "mipLevel", 0);
232
+
233
+ const void* data = nullptr;
234
+ size_t length = 0;
235
+ CopyBytesFrom(args[1], &data, &length);
236
+
237
+ // dataLayout: { offset?, bytesPerRow, rowsPerImage? }
238
+ v8::Local<v8::Object> layout = args[2].As<v8::Object>();
239
+ wgpu::TexelCopyBufferLayout data_layout = {};
240
+ data_layout.offset = U64(isolate, layout, "offset", 0);
241
+ data_layout.bytesPerRow = U32(isolate, layout, "bytesPerRow", 0);
242
+ data_layout.rowsPerImage = U32(isolate, layout, "rowsPerImage", wgpu::kCopyStrideUndefined);
243
+
244
+ wgpu::Extent3D size = ParseExtent(isolate, args[3]);
245
+
246
+ queue.WriteTexture(&copy, data, length, &data_layout, &size);
247
+ }
248
+
249
+ // queue.copyExternalImageToTexture(source, dest, copySize)
250
+ // source={source: ImageBitmap|canvas, flipY}, dest={texture, premultipliedAlpha, origin?}
251
+ // Dawn の対応 API を使わず、ソースの RGBA を writeTexture で転送する(flipY/premultiply をCPU適用)。
252
+ static void Queue_CopyExternalImageToTexture(const v8::FunctionCallbackInfo<v8::Value>& args)
253
+ {
254
+ v8::Isolate* isolate = args.GetIsolate();
255
+ wgpu::Queue& queue = Unwrap<wgpu::Queue>(args.This());
256
+
257
+ v8::Local<v8::Object> src = args[0].As<v8::Object>();
258
+ v8::Local<v8::Object> dst = args[1].As<v8::Object>();
259
+
260
+ const uint8_t* rgba = nullptr;
261
+ uint32_t sw = 0, sh = 0;
262
+ if (!GetImageSourcePixels(isolate, Prop(isolate, src, "source"), &rgba, &sw, &sh) || !rgba) {
263
+ return;
264
+ }
265
+ const bool flip_y = Bool(isolate, src, "flipY", false);
266
+ const bool premultiply = Bool(isolate, dst, "premultipliedAlpha", false);
267
+
268
+ wgpu::Extent3D size = ParseExtent(isolate, args[2]);
269
+ const uint32_t w = size.width ? size.width : sw;
270
+ const uint32_t h = size.height ? size.height : sh;
271
+
272
+ // flipY / premultiply を適用したコピーを作る
273
+ std::vector<uint8_t> buffer(static_cast<size_t>(w) * h * 4);
274
+ for (uint32_t y = 0; y < h; ++y) {
275
+ const uint32_t sy = flip_y ? (h - 1 - y) : y;
276
+ for (uint32_t x = 0; x < w; ++x) {
277
+ const uint8_t* s = &rgba[(static_cast<size_t>(std::min(sy, sh - 1)) * sw + std::min(x, sw - 1)) * 4];
278
+ uint8_t* d = &buffer[(static_cast<size_t>(y) * w + x) * 4];
279
+ if (premultiply) {
280
+ const double a = s[3] / 255.0;
281
+ d[0] = static_cast<uint8_t>(s[0] * a);
282
+ d[1] = static_cast<uint8_t>(s[1] * a);
283
+ d[2] = static_cast<uint8_t>(s[2] * a);
284
+ } else {
285
+ d[0] = s[0]; d[1] = s[1]; d[2] = s[2];
286
+ }
287
+ d[3] = s[3];
288
+ }
289
+ }
290
+
291
+ wgpu::TexelCopyTextureInfo copy = {};
292
+ copy.texture = Unwrap<wgpu::Texture>(Prop(isolate, dst, "texture").As<v8::Object>());
293
+ wgpu::TexelCopyBufferLayout layout = {};
294
+ layout.bytesPerRow = w * 4;
295
+ layout.rowsPerImage = h;
296
+ wgpu::Extent3D extent = { w, h, 1 };
297
+ queue.WriteTexture(&copy, buffer.data(), buffer.size(), &layout, &extent);
298
+ }
299
+
300
+ static void Queue_Submit(const v8::FunctionCallbackInfo<v8::Value>& args)
301
+ {
302
+ v8::Isolate* isolate = args.GetIsolate();
303
+ wgpu::Queue& queue = Unwrap<wgpu::Queue>(args.This());
304
+
305
+ std::vector<wgpu::CommandBuffer> buffers;
306
+ if (args.Length() > 0 && args[0]->IsArray()) {
307
+ auto arr = args[0].As<v8::Array>();
308
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
309
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
310
+ v8::Local<v8::Value> v;
311
+ if (arr->Get(ctx, i).ToLocal(&v) && v->IsObject()) {
312
+ buffers.push_back(Unwrap<wgpu::CommandBuffer>(v.As<v8::Object>()));
313
+ }
314
+ }
315
+ }
316
+ queue.Submit(buffers.size(), buffers.data());
317
+ }
318
+
319
+ // ---------------------------------------------------------------------------
320
+ // GPURenderPassEncoder
321
+ // ---------------------------------------------------------------------------
322
+ static void Pass_SetPipeline(const v8::FunctionCallbackInfo<v8::Value>& args)
323
+ {
324
+ Unwrap<wgpu::RenderPassEncoder>(args.This()).SetPipeline(
325
+ Unwrap<wgpu::RenderPipeline>(args[0].As<v8::Object>()));
326
+ }
327
+
328
+ static void Pass_SetBindGroup(const v8::FunctionCallbackInfo<v8::Value>& args)
329
+ {
330
+ v8::Isolate* isolate = args.GetIsolate();
331
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
332
+ uint32_t index = static_cast<uint32_t>(args[0].As<v8::Number>()->Value());
333
+ if (args.Length() < 2 || !args[1]->IsObject()) {
334
+ pass.SetBindGroup(index, nullptr);
335
+ return;
336
+ }
337
+ auto& group = Unwrap<wgpu::BindGroup>(args[1].As<v8::Object>());
338
+
339
+ // dynamicOffsets: hasDynamicOffset=true のレイアウトを使う塗り(fill_dynamic)で必須。
340
+ // 3引数目に Array<number> / Uint32Array で渡る。
341
+ std::vector<uint32_t> offsets;
342
+ if (args.Length() > 2 && !args[2]->IsNullOrUndefined()) {
343
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
344
+ if (args[2]->IsArray()) {
345
+ auto arr = args[2].As<v8::Array>();
346
+ offsets.reserve(arr->Length());
347
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
348
+ v8::Local<v8::Value> v;
349
+ if (arr->Get(ctx, i).ToLocal(&v) && v->IsNumber())
350
+ offsets.push_back(static_cast<uint32_t>(v.As<v8::Number>()->Value()));
351
+ }
352
+ } else if (args[2]->IsUint32Array()) {
353
+ auto ta = args[2].As<v8::Uint32Array>();
354
+ offsets.resize(ta->Length());
355
+ ta->CopyContents(offsets.data(), offsets.size() * sizeof(uint32_t));
356
+ }
357
+ }
358
+ pass.SetBindGroup(index, group,
359
+ static_cast<uint32_t>(offsets.size()),
360
+ offsets.empty() ? nullptr : offsets.data());
361
+ }
362
+
363
+ static void Pass_SetStencilReference(const v8::FunctionCallbackInfo<v8::Value>& args)
364
+ {
365
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
366
+ uint32_t reference = (args.Length() > 0 && args[0]->IsNumber())
367
+ ? static_cast<uint32_t>(args[0].As<v8::Number>()->Value()) : 0;
368
+ pass.SetStencilReference(reference);
369
+ }
370
+
371
+ static void Pass_SetBlendConstant(const v8::FunctionCallbackInfo<v8::Value>& args)
372
+ {
373
+ v8::Isolate* isolate = args.GetIsolate();
374
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
375
+ wgpu::Color color = {};
376
+ if (args.Length() > 0 && args[0]->IsObject()) {
377
+ auto o = args[0].As<v8::Object>();
378
+ color = { F64(isolate, o, "r"), F64(isolate, o, "g"),
379
+ F64(isolate, o, "b"), F64(isolate, o, "a") };
380
+ } else if (args.Length() > 0 && args[0]->IsArray()) {
381
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
382
+ auto c = args[0].As<v8::Array>();
383
+ auto g = [&](uint32_t k) { v8::Local<v8::Value> x;
384
+ return (c->Get(ctx, k).ToLocal(&x) && x->IsNumber()) ? x.As<v8::Number>()->Value() : 0.0; };
385
+ color = { g(0), g(1), g(2), g(3) };
386
+ }
387
+ pass.SetBlendConstant(&color);
388
+ }
389
+
390
+ static void Pass_SetVertexBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
391
+ {
392
+ v8::Isolate* isolate = args.GetIsolate();
393
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
394
+ uint32_t slot = static_cast<uint32_t>(args[0].As<v8::Number>()->Value());
395
+ auto& buffer = Unwrap<wgpu::Buffer>(args[1].As<v8::Object>());
396
+ uint64_t offset = args.Length() > 2 && args[2]->IsNumber()
397
+ ? static_cast<uint64_t>(args[2].As<v8::Number>()->Value()) : 0;
398
+ uint64_t size = args.Length() > 3 && args[3]->IsNumber()
399
+ ? static_cast<uint64_t>(args[3].As<v8::Number>()->Value()) : wgpu::kWholeSize;
400
+ (void) isolate;
401
+ pass.SetVertexBuffer(slot, buffer, offset, size);
402
+ }
403
+
404
+ static void Pass_SetIndexBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
405
+ {
406
+ v8::Isolate* isolate = args.GetIsolate();
407
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
408
+ auto& buffer = Unwrap<wgpu::Buffer>(args[0].As<v8::Object>());
409
+ wgpu::IndexFormat format = ToIndexFormat(v8util::ToStdString(isolate, args[1]));
410
+ uint64_t offset = args.Length() > 2 && args[2]->IsNumber()
411
+ ? static_cast<uint64_t>(args[2].As<v8::Number>()->Value()) : 0;
412
+ uint64_t size = args.Length() > 3 && args[3]->IsNumber()
413
+ ? static_cast<uint64_t>(args[3].As<v8::Number>()->Value()) : wgpu::kWholeSize;
414
+ pass.SetIndexBuffer(buffer, format, offset, size);
415
+ }
416
+
417
+ static void Pass_SetViewport(const v8::FunctionCallbackInfo<v8::Value>& args)
418
+ {
419
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
420
+ auto n = [&](int i) { return static_cast<float>(args[i].As<v8::Number>()->Value()); };
421
+ pass.SetViewport(n(0), n(1), n(2), n(3), n(4), n(5));
422
+ }
423
+
424
+ static void Pass_SetScissorRect(const v8::FunctionCallbackInfo<v8::Value>& args)
425
+ {
426
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
427
+ auto n = [&](int i) { return static_cast<uint32_t>(args[i].As<v8::Number>()->Value()); };
428
+ pass.SetScissorRect(n(0), n(1), n(2), n(3));
429
+ }
430
+
431
+ static void Pass_Draw(const v8::FunctionCallbackInfo<v8::Value>& args)
432
+ {
433
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
434
+ auto n = [&](int i, uint32_t fb) {
435
+ return (i < args.Length() && args[i]->IsNumber())
436
+ ? static_cast<uint32_t>(args[i].As<v8::Number>()->Value()) : fb; };
437
+ pass.Draw(n(0, 0), n(1, 1), n(2, 0), n(3, 0));
438
+ }
439
+
440
+ static void Pass_DrawIndexed(const v8::FunctionCallbackInfo<v8::Value>& args)
441
+ {
442
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
443
+ auto n = [&](int i, uint32_t fb) {
444
+ return (i < args.Length() && args[i]->IsNumber())
445
+ ? static_cast<uint32_t>(args[i].As<v8::Number>()->Value()) : fb; };
446
+ pass.DrawIndexed(n(0, 0), n(1, 1), n(2, 0), n(3, 0), n(4, 0));
447
+ }
448
+
449
+ static void Pass_DrawIndirect(const v8::FunctionCallbackInfo<v8::Value>& args)
450
+ {
451
+ auto& pass = Unwrap<wgpu::RenderPassEncoder>(args.This());
452
+ auto& buffer = Unwrap<wgpu::Buffer>(args[0].As<v8::Object>());
453
+ uint64_t offset = args.Length() > 1 && args[1]->IsNumber()
454
+ ? static_cast<uint64_t>(args[1].As<v8::Number>()->Value()) : 0;
455
+ pass.DrawIndirect(buffer, offset);
456
+ }
457
+
458
+ static void Pass_End(const v8::FunctionCallbackInfo<v8::Value>& args)
459
+ {
460
+ Unwrap<wgpu::RenderPassEncoder>(args.This()).End();
461
+ }
462
+
463
+ static v8::Local<v8::Object> WrapRenderPass(v8::Isolate* isolate, wgpu::RenderPassEncoder pass)
464
+ {
465
+ v8::Local<v8::Object> obj = WrapWith(isolate, Tmpl(isolate).renderPass, std::move(pass));
466
+ SetMethod(isolate, obj, "setPipeline", Pass_SetPipeline);
467
+ SetMethod(isolate, obj, "setBindGroup", Pass_SetBindGroup);
468
+ SetMethod(isolate, obj, "setVertexBuffer", Pass_SetVertexBuffer);
469
+ SetMethod(isolate, obj, "setIndexBuffer", Pass_SetIndexBuffer);
470
+ SetMethod(isolate, obj, "setViewport", Pass_SetViewport);
471
+ SetMethod(isolate, obj, "setScissorRect", Pass_SetScissorRect);
472
+ SetMethod(isolate, obj, "setStencilReference", Pass_SetStencilReference);
473
+ SetMethod(isolate, obj, "setBlendConstant", Pass_SetBlendConstant);
474
+ SetMethod(isolate, obj, "draw", Pass_Draw);
475
+ SetMethod(isolate, obj, "drawIndexed", Pass_DrawIndexed);
476
+ SetMethod(isolate, obj, "drawIndirect", Pass_DrawIndirect);
477
+ SetMethod(isolate, obj, "end", Pass_End);
478
+ return obj;
479
+ }
480
+
481
+ // ---------------------------------------------------------------------------
482
+ // GPUCommandEncoder
483
+ // ---------------------------------------------------------------------------
484
+ static void Encoder_BeginRenderPass(const v8::FunctionCallbackInfo<v8::Value>& args)
485
+ {
486
+ v8::Isolate* isolate = args.GetIsolate();
487
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
488
+ auto& encoder = Unwrap<wgpu::CommandEncoder>(args.This());
489
+ v8::Local<v8::Object> desc = args[0].As<v8::Object>();
490
+
491
+ std::vector<wgpu::RenderPassColorAttachment> color_attachments;
492
+ v8::Local<v8::Value> ca = Prop(isolate, desc, "colorAttachments");
493
+ if (ca->IsArray()) {
494
+ auto arr = ca.As<v8::Array>();
495
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
496
+ v8::Local<v8::Value> v;
497
+ if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
498
+ auto o = v.As<v8::Object>();
499
+ wgpu::RenderPassColorAttachment att = {};
500
+ att.view = Unwrap<wgpu::TextureView>(Prop(isolate, o, "view").As<v8::Object>());
501
+ att.loadOp = ToLoadOp(webgpu::Str(isolate, o, "loadOp"));
502
+ att.storeOp = ToStoreOp(webgpu::Str(isolate, o, "storeOp"));
503
+ att.depthSlice = wgpu::kDepthSliceUndefined;
504
+ v8::Local<v8::Value> cv = Prop(isolate, o, "clearValue");
505
+ if (cv->IsArray()) {
506
+ auto c = cv.As<v8::Array>();
507
+ auto g = [&](uint32_t k) { v8::Local<v8::Value> x;
508
+ return (c->Get(ctx, k).ToLocal(&x) && x->IsNumber()) ? x.As<v8::Number>()->Value() : 0.0; };
509
+ att.clearValue = { g(0), g(1), g(2), g(3) };
510
+ } else if (cv->IsObject()) {
511
+ auto c = cv.As<v8::Object>();
512
+ att.clearValue = { F64(isolate, c, "r"), F64(isolate, c, "g"),
513
+ F64(isolate, c, "b"), F64(isolate, c, "a") };
514
+ }
515
+ color_attachments.push_back(att);
516
+ }
517
+ }
518
+
519
+ wgpu::RenderPassDescriptor pass_desc = {};
520
+ pass_desc.colorAttachmentCount = color_attachments.size();
521
+ pass_desc.colorAttachments = color_attachments.data();
522
+
523
+ // depthStencilAttachment
524
+ wgpu::RenderPassDepthStencilAttachment depth = {};
525
+ v8::Local<v8::Value> ds = Prop(isolate, desc, "depthStencilAttachment");
526
+ if (ds->IsObject()) {
527
+ auto o = ds.As<v8::Object>();
528
+ depth.view = Unwrap<wgpu::TextureView>(Prop(isolate, o, "view").As<v8::Object>());
529
+ if (HasProp(isolate, o, "depthLoadOp")) {
530
+ depth.depthLoadOp = ToLoadOp(webgpu::Str(isolate, o, "depthLoadOp"));
531
+ depth.depthStoreOp = ToStoreOp(webgpu::Str(isolate, o, "depthStoreOp"));
532
+ depth.depthClearValue = static_cast<float>(F64(isolate, o, "depthClearValue", 1.0));
533
+ }
534
+ // stencil アスペクト (マスク描画で使用)
535
+ if (HasProp(isolate, o, "stencilLoadOp")) {
536
+ depth.stencilLoadOp = ToLoadOp(webgpu::Str(isolate, o, "stencilLoadOp"));
537
+ depth.stencilStoreOp = ToStoreOp(webgpu::Str(isolate, o, "stencilStoreOp"));
538
+ depth.stencilClearValue = U32(isolate, o, "stencilClearValue", 0);
539
+ }
540
+ if (Bool(isolate, o, "depthReadOnly", false))
541
+ depth.depthReadOnly = true;
542
+ if (Bool(isolate, o, "stencilReadOnly", false))
543
+ depth.stencilReadOnly = true;
544
+ pass_desc.depthStencilAttachment = &depth;
545
+ }
546
+
547
+ args.GetReturnValue().Set(
548
+ WrapRenderPass(isolate, encoder.BeginRenderPass(&pass_desc)));
549
+ }
550
+
551
+ static void Encoder_Finish(const v8::FunctionCallbackInfo<v8::Value>& args)
552
+ {
553
+ v8::Isolate* isolate = args.GetIsolate();
554
+ args.GetReturnValue().Set(
555
+ WrapWith(isolate, Tmpl(isolate).commandBuffer,
556
+ Unwrap<wgpu::CommandEncoder>(args.This()).Finish()));
557
+ }
558
+
559
+ static void Encoder_CopyBufferToBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
560
+ {
561
+ auto& encoder = Unwrap<wgpu::CommandEncoder>(args.This());
562
+ auto& src = Unwrap<wgpu::Buffer>(args[0].As<v8::Object>());
563
+ uint64_t src_off = static_cast<uint64_t>(args[1].As<v8::Number>()->Value());
564
+ auto& dst = Unwrap<wgpu::Buffer>(args[2].As<v8::Object>());
565
+ uint64_t dst_off = static_cast<uint64_t>(args[3].As<v8::Number>()->Value());
566
+ uint64_t size = static_cast<uint64_t>(args[4].As<v8::Number>()->Value());
567
+ encoder.CopyBufferToBuffer(src, src_off, dst, dst_off, size);
568
+ }
569
+
570
+ // {texture, mipLevel?, origin?} を wgpu::TexelCopyTextureInfo へ
571
+ static wgpu::TexelCopyTextureInfo ParseTexelCopyTexture(v8::Isolate* isolate, v8::Local<v8::Object> o)
572
+ {
573
+ wgpu::TexelCopyTextureInfo info = {};
574
+ info.texture = Unwrap<wgpu::Texture>(Prop(isolate, o, "texture").As<v8::Object>());
575
+ info.mipLevel = U32(isolate, o, "mipLevel", 0);
576
+ v8::Local<v8::Value> origin = Prop(isolate, o, "origin");
577
+ if (origin->IsObject() || origin->IsArray()) {
578
+ wgpu::Extent3D e = ParseExtent(isolate, origin);
579
+ info.origin = { e.width, e.height, e.depthOrArrayLayers };
580
+ }
581
+ return info;
582
+ }
583
+
584
+ static void Encoder_CopyTextureToTexture(const v8::FunctionCallbackInfo<v8::Value>& args)
585
+ {
586
+ v8::Isolate* isolate = args.GetIsolate();
587
+ auto& encoder = Unwrap<wgpu::CommandEncoder>(args.This());
588
+ wgpu::TexelCopyTextureInfo src = ParseTexelCopyTexture(isolate, args[0].As<v8::Object>());
589
+ wgpu::TexelCopyTextureInfo dst = ParseTexelCopyTexture(isolate, args[1].As<v8::Object>());
590
+ wgpu::Extent3D size = ParseExtent(isolate, args[2]);
591
+ encoder.CopyTextureToTexture(&src, &dst, &size);
592
+ }
593
+
594
+ static void Encoder_CopyTextureToBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
595
+ {
596
+ v8::Isolate* isolate = args.GetIsolate();
597
+ auto& encoder = Unwrap<wgpu::CommandEncoder>(args.This());
598
+ wgpu::TexelCopyTextureInfo src = ParseTexelCopyTexture(isolate, args[0].As<v8::Object>());
599
+
600
+ v8::Local<v8::Object> d = args[1].As<v8::Object>();
601
+ wgpu::TexelCopyBufferInfo dst = {};
602
+ dst.buffer = Unwrap<wgpu::Buffer>(Prop(isolate, d, "buffer").As<v8::Object>());
603
+ dst.layout.offset = U64(isolate, d, "offset", 0);
604
+ dst.layout.bytesPerRow = U32(isolate, d, "bytesPerRow", 0);
605
+ dst.layout.rowsPerImage = U32(isolate, d, "rowsPerImage", wgpu::kCopyStrideUndefined);
606
+
607
+ wgpu::Extent3D size = ParseExtent(isolate, args[2]);
608
+ encoder.CopyTextureToBuffer(&src, &dst, &size);
609
+ }
610
+
611
+ // ---------------------------------------------------------------------------
612
+ // GPUCanvasContext
613
+ // ---------------------------------------------------------------------------
614
+ static void Ctx_Configure(const v8::FunctionCallbackInfo<v8::Value>& args)
615
+ {
616
+ v8::Isolate* isolate = args.GetIsolate();
617
+ DawnContext* gpu = HostContext::From(isolate)->gpu;
618
+ // device は既に DawnContext が保持。フォーマット/サイズのみ反映する。
619
+ if (args.Length() > 0 && args[0]->IsObject()) {
620
+ // width/height は canvas サイズを使用 (configure の size は任意)
621
+ gpu->Configure(gpu->width(), gpu->height());
622
+ }
623
+ }
624
+
625
+ static void Ctx_GetCurrentTexture(const v8::FunctionCallbackInfo<v8::Value>& args)
626
+ {
627
+ v8::Isolate* isolate = args.GetIsolate();
628
+ DawnContext* gpu = HostContext::From(isolate)->gpu;
629
+ args.GetReturnValue().Set(WrapTexture(isolate, gpu->GetCurrentTexture()));
630
+ }
631
+
632
+ static void Ctx_GetConfiguredFormat(const v8::FunctionCallbackInfo<v8::Value>& args)
633
+ {
634
+ // getPreferredCanvasFormat 相当。DawnContext の surface フォーマットを返す。
635
+ v8::Isolate* isolate = args.GetIsolate();
636
+ DawnContext* gpu = HostContext::From(isolate)->gpu;
637
+ const char* fmt = (gpu->format() == wgpu::TextureFormat::RGBA8Unorm)
638
+ ? "rgba8unorm" : "bgra8unorm";
639
+ args.GetReturnValue().Set(v8util::Str(isolate, fmt));
640
+ }
641
+
642
+ // タグ付きラップ (bind group で resource 種別を判定するため)
643
+ static v8::Local<v8::Object> WrapTextureView(v8::Isolate* isolate, wgpu::TextureView view)
644
+ {
645
+ auto obj = WrapWith(isolate, Tmpl(isolate).textureView, std::move(view));
646
+ SetValue(isolate, obj, "__gpuType", Str(isolate, "textureView"));
647
+ return obj;
648
+ }
649
+
650
+ static v8::Local<v8::Object> WrapSampler(v8::Isolate* isolate, wgpu::Sampler sampler)
651
+ {
652
+ auto obj = WrapWith(isolate, Tmpl(isolate).sampler, std::move(sampler));
653
+ SetValue(isolate, obj, "__gpuType", Str(isolate, "sampler"));
654
+ return obj;
655
+ }
656
+
657
+ // ---------------------------------------------------------------------------
658
+ // GPUDevice.createBuffer
659
+ // ---------------------------------------------------------------------------
660
+ static void Device_CreateBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
661
+ {
662
+ v8::Isolate* isolate = args.GetIsolate();
663
+ auto& device = Unwrap<wgpu::Device>(args.This());
664
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
665
+
666
+ wgpu::BufferDescriptor desc = {};
667
+ desc.size = U64(isolate, d, "size", 0);
668
+ desc.usage = static_cast<wgpu::BufferUsage>(U32(isolate, d, "usage", 0));
669
+ desc.mappedAtCreation = Bool(isolate, d, "mappedAtCreation", false);
670
+
671
+ args.GetReturnValue().Set(WrapBuffer(isolate, device.CreateBuffer(&desc)));
672
+ }
673
+
674
+ // ---------------------------------------------------------------------------
675
+ // GPUDevice.createTexture
676
+ // ---------------------------------------------------------------------------
677
+ static void Device_CreateTexture(const v8::FunctionCallbackInfo<v8::Value>& args)
678
+ {
679
+ v8::Isolate* isolate = args.GetIsolate();
680
+ auto& device = Unwrap<wgpu::Device>(args.This());
681
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
682
+
683
+ wgpu::TextureDescriptor desc = {};
684
+ desc.size = ParseExtent(isolate, Prop(isolate, d, "size"));
685
+ desc.format = ToTextureFormat(webgpu::Str(isolate, d, "format"));
686
+ desc.usage = static_cast<wgpu::TextureUsage>(U32(isolate, d, "usage", 0));
687
+ desc.dimension = HasProp(isolate, d, "dimension")
688
+ ? ToTextureDimension(webgpu::Str(isolate, d, "dimension"))
689
+ : wgpu::TextureDimension::e2D;
690
+ desc.mipLevelCount = U32(isolate, d, "mipLevelCount", 1);
691
+ desc.sampleCount = U32(isolate, d, "sampleCount", 1);
692
+
693
+ args.GetReturnValue().Set(WrapTexture(isolate, device.CreateTexture(&desc)));
694
+ }
695
+
696
+ // ---------------------------------------------------------------------------
697
+ // GPUDevice.createSampler
698
+ // ---------------------------------------------------------------------------
699
+ static void Device_CreateSampler(const v8::FunctionCallbackInfo<v8::Value>& args)
700
+ {
701
+ v8::Isolate* isolate = args.GetIsolate();
702
+ auto& device = Unwrap<wgpu::Device>(args.This());
703
+
704
+ wgpu::SamplerDescriptor desc = {};
705
+ if (args.Length() > 0 && args[0]->IsObject()) {
706
+ auto d = args[0].As<v8::Object>();
707
+ desc.addressModeU = ToAddressMode(webgpu::Str(isolate, d, "addressModeU"));
708
+ desc.addressModeV = ToAddressMode(webgpu::Str(isolate, d, "addressModeV"));
709
+ desc.addressModeW = ToAddressMode(webgpu::Str(isolate, d, "addressModeW"));
710
+ desc.magFilter = ToFilterMode(webgpu::Str(isolate, d, "magFilter"));
711
+ desc.minFilter = ToFilterMode(webgpu::Str(isolate, d, "minFilter"));
712
+ desc.mipmapFilter = ToMipmapFilterMode(webgpu::Str(isolate, d, "mipmapFilter"));
713
+ if (HasProp(isolate, d, "lodMinClamp")) desc.lodMinClamp = static_cast<float>(F64(isolate, d, "lodMinClamp"));
714
+ if (HasProp(isolate, d, "lodMaxClamp")) desc.lodMaxClamp = static_cast<float>(F64(isolate, d, "lodMaxClamp", 32.f));
715
+ if (HasProp(isolate, d, "maxAnisotropy")) desc.maxAnisotropy = static_cast<uint16_t>(U32(isolate, d, "maxAnisotropy", 1));
716
+ }
717
+ args.GetReturnValue().Set(WrapSampler(isolate, device.CreateSampler(&desc)));
718
+ }
719
+
720
+ // ---------------------------------------------------------------------------
721
+ // GPUDevice.createShaderModule
722
+ // ---------------------------------------------------------------------------
723
+ static void Device_CreateShaderModule(const v8::FunctionCallbackInfo<v8::Value>& args)
724
+ {
725
+ v8::Isolate* isolate = args.GetIsolate();
726
+ auto& device = Unwrap<wgpu::Device>(args.This());
727
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
728
+
729
+ const std::string code = webgpu::Str(isolate, d, "code");
730
+
731
+ wgpu::ShaderSourceWGSL wgsl = {};
732
+ wgsl.code = wgpu::StringView(code.c_str(), code.size());
733
+
734
+ wgpu::ShaderModuleDescriptor desc = {};
735
+ desc.nextInChain = &wgsl;
736
+
737
+ args.GetReturnValue().Set(
738
+ WrapWith(isolate, Tmpl(isolate).shaderModule, device.CreateShaderModule(&desc)));
739
+ }
740
+
741
+ // ---------------------------------------------------------------------------
742
+ // GPUDevice.createBindGroupLayout
743
+ // ---------------------------------------------------------------------------
744
+ static void Device_CreateBindGroupLayout(const v8::FunctionCallbackInfo<v8::Value>& args)
745
+ {
746
+ v8::Isolate* isolate = args.GetIsolate();
747
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
748
+ auto& device = Unwrap<wgpu::Device>(args.This());
749
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
750
+
751
+ std::vector<wgpu::BindGroupLayoutEntry> entries;
752
+ v8::Local<v8::Value> ev = Prop(isolate, d, "entries");
753
+ if (ev->IsArray()) {
754
+ auto arr = ev.As<v8::Array>();
755
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
756
+ v8::Local<v8::Value> v;
757
+ if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
758
+ auto o = v.As<v8::Object>();
759
+ wgpu::BindGroupLayoutEntry entry = {};
760
+ entry.binding = U32(isolate, o, "binding", 0);
761
+ entry.visibility = static_cast<wgpu::ShaderStage>(U32(isolate, o, "visibility", 0));
762
+
763
+ if (HasProp(isolate, o, "buffer")) {
764
+ auto b = Prop(isolate, o, "buffer").As<v8::Object>();
765
+ entry.buffer.type = ToBufferBindingType(webgpu::Str(isolate, b, "type"));
766
+ entry.buffer.hasDynamicOffset = Bool(isolate, b, "hasDynamicOffset", false);
767
+ entry.buffer.minBindingSize = U64(isolate, b, "minBindingSize", 0);
768
+ } else if (HasProp(isolate, o, "sampler")) {
769
+ auto s = Prop(isolate, o, "sampler").As<v8::Object>();
770
+ entry.sampler.type = ToSamplerBindingType(webgpu::Str(isolate, s, "type"));
771
+ } else if (HasProp(isolate, o, "texture")) {
772
+ auto t = Prop(isolate, o, "texture").As<v8::Object>();
773
+ entry.texture.sampleType = ToTextureSampleType(webgpu::Str(isolate, t, "sampleType"));
774
+ entry.texture.viewDimension = HasProp(isolate, t, "viewDimension")
775
+ ? ToTextureViewDimension(webgpu::Str(isolate, t, "viewDimension"))
776
+ : wgpu::TextureViewDimension::e2D;
777
+ entry.texture.multisampled = Bool(isolate, t, "multisampled", false);
778
+ }
779
+ // «EXTEND» storageTexture / externalTexture は必要に応じて追加
780
+ entries.push_back(entry);
781
+ }
782
+ }
783
+
784
+ wgpu::BindGroupLayoutDescriptor desc = {};
785
+ desc.entryCount = entries.size();
786
+ desc.entries = entries.data();
787
+
788
+ args.GetReturnValue().Set(
789
+ WrapWith(isolate, Tmpl(isolate).bindGroupLayout, device.CreateBindGroupLayout(&desc)));
790
+ }
791
+
792
+ // ---------------------------------------------------------------------------
793
+ // GPUDevice.createBindGroup
794
+ // ---------------------------------------------------------------------------
795
+ static void Device_CreateBindGroup(const v8::FunctionCallbackInfo<v8::Value>& args)
796
+ {
797
+ v8::Isolate* isolate = args.GetIsolate();
798
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
799
+ auto& device = Unwrap<wgpu::Device>(args.This());
800
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
801
+
802
+ std::vector<wgpu::BindGroupEntry> entries;
803
+ v8::Local<v8::Value> ev = Prop(isolate, d, "entries");
804
+ if (ev->IsArray()) {
805
+ auto arr = ev.As<v8::Array>();
806
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
807
+ v8::Local<v8::Value> v;
808
+ if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
809
+ auto o = v.As<v8::Object>();
810
+ wgpu::BindGroupEntry entry = {};
811
+ entry.binding = U32(isolate, o, "binding", 0);
812
+
813
+ v8::Local<v8::Value> res = Prop(isolate, o, "resource");
814
+ if (res->IsObject()) {
815
+ auto ro = res.As<v8::Object>();
816
+ if (HasProp(isolate, ro, "buffer")) {
817
+ entry.buffer = Unwrap<wgpu::Buffer>(Prop(isolate, ro, "buffer").As<v8::Object>());
818
+ entry.offset = U64(isolate, ro, "offset", 0);
819
+ if (HasProp(isolate, ro, "size")) entry.size = U64(isolate, ro, "size", 0);
820
+ else entry.size = wgpu::kWholeSize;
821
+ } else {
822
+ const std::string type = webgpu::Str(isolate, ro, "__gpuType");
823
+ if (type == "sampler") {
824
+ entry.sampler = Unwrap<wgpu::Sampler>(ro);
825
+ } else {
826
+ entry.textureView = Unwrap<wgpu::TextureView>(ro);
827
+ }
828
+ }
829
+ }
830
+ entries.push_back(entry);
831
+ }
832
+ }
833
+
834
+ wgpu::BindGroupDescriptor desc = {};
835
+ desc.layout = Unwrap<wgpu::BindGroupLayout>(Prop(isolate, d, "layout").As<v8::Object>());
836
+ desc.entryCount = entries.size();
837
+ desc.entries = entries.data();
838
+
839
+ args.GetReturnValue().Set(
840
+ WrapWith(isolate, Tmpl(isolate).bindGroup, device.CreateBindGroup(&desc)));
841
+ }
842
+
843
+ // ---------------------------------------------------------------------------
844
+ // GPUDevice.createPipelineLayout
845
+ // ---------------------------------------------------------------------------
846
+ static void Device_CreatePipelineLayout(const v8::FunctionCallbackInfo<v8::Value>& args)
847
+ {
848
+ v8::Isolate* isolate = args.GetIsolate();
849
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
850
+ auto& device = Unwrap<wgpu::Device>(args.This());
851
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
852
+
853
+ std::vector<wgpu::BindGroupLayout> layouts;
854
+ v8::Local<v8::Value> lv = Prop(isolate, d, "bindGroupLayouts");
855
+ if (lv->IsArray()) {
856
+ auto arr = lv.As<v8::Array>();
857
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
858
+ v8::Local<v8::Value> v;
859
+ if (arr->Get(ctx, i).ToLocal(&v) && v->IsObject()) {
860
+ layouts.push_back(Unwrap<wgpu::BindGroupLayout>(v.As<v8::Object>()));
861
+ }
862
+ }
863
+ }
864
+
865
+ wgpu::PipelineLayoutDescriptor desc = {};
866
+ desc.bindGroupLayoutCount = layouts.size();
867
+ desc.bindGroupLayouts = layouts.data();
868
+
869
+ args.GetReturnValue().Set(
870
+ WrapWith(isolate, Tmpl(isolate).pipelineLayout, device.CreatePipelineLayout(&desc)));
871
+ }
872
+
873
+ // ---------------------------------------------------------------------------
874
+ // GPUDevice.createRenderPipeline
875
+ // ---------------------------------------------------------------------------
876
+ // ディスクリプタ内で参照する一時ストレージ (CreateRenderPipeline は同期コピーするため
877
+ // 呼び出しが終わるまでこれらの vector を生存させる)。
878
+ struct RenderPipelineScratch {
879
+ std::vector<std::vector<wgpu::VertexAttribute>> attributes;
880
+ std::vector<wgpu::VertexBufferLayout> vertex_buffers;
881
+ std::vector<wgpu::ColorTargetState> targets;
882
+ std::vector<wgpu::BlendState> blends;
883
+ std::string vs_entry, fs_entry;
884
+ };
885
+
886
+ static void Device_CreateRenderPipeline(const v8::FunctionCallbackInfo<v8::Value>& args)
887
+ {
888
+ v8::Isolate* isolate = args.GetIsolate();
889
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
890
+ auto& device = Unwrap<wgpu::Device>(args.This());
891
+ v8::Local<v8::Object> d = args[0].As<v8::Object>();
892
+
893
+ RenderPipelineScratch scratch;
894
+ wgpu::RenderPipelineDescriptor desc = {};
895
+
896
+ // layout: 'auto' or GPUPipelineLayout
897
+ v8::Local<v8::Value> layout = Prop(isolate, d, "layout");
898
+ if (layout->IsObject()) {
899
+ desc.layout = Unwrap<wgpu::PipelineLayout>(layout.As<v8::Object>());
900
+ } // 'auto' の場合は nullptr のまま
901
+
902
+ // vertex
903
+ v8::Local<v8::Object> vertex = Prop(isolate, d, "vertex").As<v8::Object>();
904
+ desc.vertex.module = Unwrap<wgpu::ShaderModule>(Prop(isolate, vertex, "module").As<v8::Object>());
905
+ scratch.vs_entry = webgpu::Str(isolate, vertex, "entryPoint");
906
+ if (!scratch.vs_entry.empty()) {
907
+ desc.vertex.entryPoint = wgpu::StringView(scratch.vs_entry.c_str(), scratch.vs_entry.size());
908
+ }
909
+
910
+ v8::Local<v8::Value> vbs = Prop(isolate, vertex, "buffers");
911
+ if (vbs->IsArray()) {
912
+ auto arr = vbs.As<v8::Array>();
913
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
914
+ v8::Local<v8::Value> v;
915
+ if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
916
+ auto o = v.As<v8::Object>();
917
+
918
+ std::vector<wgpu::VertexAttribute> attrs;
919
+ v8::Local<v8::Value> av = Prop(isolate, o, "attributes");
920
+ if (av->IsArray()) {
921
+ auto aarr = av.As<v8::Array>();
922
+ for (uint32_t j = 0; j < aarr->Length(); ++j) {
923
+ v8::Local<v8::Value> a;
924
+ if (!aarr->Get(ctx, j).ToLocal(&a) || !a->IsObject()) continue;
925
+ auto ao = a.As<v8::Object>();
926
+ wgpu::VertexAttribute attr = {};
927
+ attr.format = ToVertexFormat(webgpu::Str(isolate, ao, "format"));
928
+ attr.offset = U64(isolate, ao, "offset", 0);
929
+ attr.shaderLocation = U32(isolate, ao, "shaderLocation", 0);
930
+ attrs.push_back(attr);
931
+ }
932
+ }
933
+ scratch.attributes.push_back(std::move(attrs));
934
+
935
+ wgpu::VertexBufferLayout vbl = {};
936
+ vbl.arrayStride = U64(isolate, o, "arrayStride", 0);
937
+ vbl.stepMode = HasProp(isolate, o, "stepMode")
938
+ ? ToStepMode(webgpu::Str(isolate, o, "stepMode")) : wgpu::VertexStepMode::Vertex;
939
+ vbl.attributeCount = scratch.attributes.back().size();
940
+ vbl.attributes = scratch.attributes.back().data();
941
+ scratch.vertex_buffers.push_back(vbl);
942
+ }
943
+ desc.vertex.bufferCount = scratch.vertex_buffers.size();
944
+ desc.vertex.buffers = scratch.vertex_buffers.data();
945
+ }
946
+
947
+ // primitive
948
+ v8::Local<v8::Value> prim = Prop(isolate, d, "primitive");
949
+ if (prim->IsObject()) {
950
+ auto p = prim.As<v8::Object>();
951
+ desc.primitive.topology = ToTopology(webgpu::Str(isolate, p, "topology"));
952
+ if (HasProp(isolate, p, "stripIndexFormat"))
953
+ desc.primitive.stripIndexFormat = ToIndexFormat(webgpu::Str(isolate, p, "stripIndexFormat"));
954
+ if (HasProp(isolate, p, "frontFace"))
955
+ desc.primitive.frontFace = ToFrontFace(webgpu::Str(isolate, p, "frontFace"));
956
+ if (HasProp(isolate, p, "cullMode"))
957
+ desc.primitive.cullMode = ToCullMode(webgpu::Str(isolate, p, "cullMode"));
958
+ }
959
+
960
+ // depthStencil (マスク処理は stencil8 + stencilFront/Back を使う)
961
+ wgpu::DepthStencilState depth = {};
962
+ v8::Local<v8::Value> dsv = Prop(isolate, d, "depthStencil");
963
+ if (dsv->IsObject()) {
964
+ auto ds = dsv.As<v8::Object>();
965
+ depth.format = ToTextureFormat(webgpu::Str(isolate, ds, "format"));
966
+
967
+ // depth アスペクトを持つフォーマットのときのみ depthWriteEnabled/depthCompare を設定する。
968
+ // stencil8 (depth なし) では両者を未設定(Undefined)のままにしないと検証エラーになる。
969
+ const bool has_depth = depth.format == wgpu::TextureFormat::Depth16Unorm
970
+ || depth.format == wgpu::TextureFormat::Depth24Plus
971
+ || depth.format == wgpu::TextureFormat::Depth24PlusStencil8
972
+ || depth.format == wgpu::TextureFormat::Depth32Float
973
+ || depth.format == wgpu::TextureFormat::Depth32FloatStencil8;
974
+ if (has_depth) {
975
+ depth.depthWriteEnabled = Bool(isolate, ds, "depthWriteEnabled", false)
976
+ ? wgpu::OptionalBool::True : wgpu::OptionalBool::False;
977
+ depth.depthCompare = HasProp(isolate, ds, "depthCompare")
978
+ ? ToCompareFunction(webgpu::Str(isolate, ds, "depthCompare"))
979
+ : wgpu::CompareFunction::Always;
980
+ }
981
+
982
+ auto parseFace = [&](const char* key, wgpu::StencilFaceState& face) {
983
+ v8::Local<v8::Value> fv = Prop(isolate, ds, key);
984
+ if (!fv->IsObject()) return;
985
+ auto f = fv.As<v8::Object>();
986
+ if (HasProp(isolate, f, "compare"))
987
+ face.compare = ToCompareFunction(webgpu::Str(isolate, f, "compare"));
988
+ if (HasProp(isolate, f, "failOp"))
989
+ face.failOp = ToStencilOperation(webgpu::Str(isolate, f, "failOp"));
990
+ if (HasProp(isolate, f, "depthFailOp"))
991
+ face.depthFailOp = ToStencilOperation(webgpu::Str(isolate, f, "depthFailOp"));
992
+ if (HasProp(isolate, f, "passOp"))
993
+ face.passOp = ToStencilOperation(webgpu::Str(isolate, f, "passOp"));
994
+ };
995
+ parseFace("stencilFront", depth.stencilFront);
996
+ parseFace("stencilBack", depth.stencilBack);
997
+
998
+ if (HasProp(isolate, ds, "stencilReadMask"))
999
+ depth.stencilReadMask = U32(isolate, ds, "stencilReadMask", 0xFFFFFFFF);
1000
+ if (HasProp(isolate, ds, "stencilWriteMask"))
1001
+ depth.stencilWriteMask = U32(isolate, ds, "stencilWriteMask", 0xFFFFFFFF);
1002
+
1003
+ desc.depthStencil = &depth;
1004
+ }
1005
+
1006
+ // multisample
1007
+ v8::Local<v8::Value> ms = Prop(isolate, d, "multisample");
1008
+ if (ms->IsObject()) {
1009
+ auto m = ms.As<v8::Object>();
1010
+ desc.multisample.count = U32(isolate, m, "count", 1);
1011
+ }
1012
+
1013
+ // fragment
1014
+ wgpu::FragmentState fragment = {};
1015
+ v8::Local<v8::Value> fv = Prop(isolate, d, "fragment");
1016
+ if (fv->IsObject()) {
1017
+ auto f = fv.As<v8::Object>();
1018
+ fragment.module = Unwrap<wgpu::ShaderModule>(Prop(isolate, f, "module").As<v8::Object>());
1019
+ scratch.fs_entry = webgpu::Str(isolate, f, "entryPoint");
1020
+ if (!scratch.fs_entry.empty()) {
1021
+ fragment.entryPoint = wgpu::StringView(scratch.fs_entry.c_str(), scratch.fs_entry.size());
1022
+ }
1023
+
1024
+ v8::Local<v8::Value> tv = Prop(isolate, f, "targets");
1025
+ if (tv->IsArray()) {
1026
+ auto arr = tv.As<v8::Array>();
1027
+ scratch.blends.resize(arr->Length());
1028
+ for (uint32_t i = 0; i < arr->Length(); ++i) {
1029
+ v8::Local<v8::Value> v;
1030
+ if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) { scratch.targets.push_back({}); continue; }
1031
+ auto o = v.As<v8::Object>();
1032
+ wgpu::ColorTargetState target = {};
1033
+ target.format = ToTextureFormat(webgpu::Str(isolate, o, "format"));
1034
+ if (HasProp(isolate, o, "writeMask"))
1035
+ target.writeMask = static_cast<wgpu::ColorWriteMask>(U32(isolate, o, "writeMask", 0xF));
1036
+
1037
+ v8::Local<v8::Value> bv = Prop(isolate, o, "blend");
1038
+ if (bv->IsObject()) {
1039
+ auto b = bv.As<v8::Object>();
1040
+ wgpu::BlendState& blend = scratch.blends[i];
1041
+ auto color = Prop(isolate, b, "color").As<v8::Object>();
1042
+ auto alpha = Prop(isolate, b, "alpha").As<v8::Object>();
1043
+ blend.color.srcFactor = ToBlendFactor(webgpu::Str(isolate, color, "srcFactor"));
1044
+ blend.color.dstFactor = ToBlendFactor(webgpu::Str(isolate, color, "dstFactor"));
1045
+ blend.color.operation = ToBlendOperation(webgpu::Str(isolate, color, "operation"));
1046
+ blend.alpha.srcFactor = ToBlendFactor(webgpu::Str(isolate, alpha, "srcFactor"));
1047
+ blend.alpha.dstFactor = ToBlendFactor(webgpu::Str(isolate, alpha, "dstFactor"));
1048
+ blend.alpha.operation = ToBlendOperation(webgpu::Str(isolate, alpha, "operation"));
1049
+ target.blend = &blend;
1050
+ }
1051
+ scratch.targets.push_back(target);
1052
+ }
1053
+ fragment.targetCount = scratch.targets.size();
1054
+ fragment.targets = scratch.targets.data();
1055
+ }
1056
+ desc.fragment = &fragment;
1057
+ }
1058
+
1059
+ args.GetReturnValue().Set(WrapPipeline(isolate, device.CreateRenderPipeline(&desc)));
1060
+ }
1061
+
1062
+ static void Device_CreateCommandEncoder(const v8::FunctionCallbackInfo<v8::Value>& args)
1063
+ {
1064
+ v8::Isolate* isolate = args.GetIsolate();
1065
+ auto& device = Unwrap<wgpu::Device>(args.This());
1066
+ args.GetReturnValue().Set(WrapEncoder(isolate, device.CreateCommandEncoder()));
1067
+ }
1068
+
1069
+ static void Pipeline_GetBindGroupLayout(const v8::FunctionCallbackInfo<v8::Value>& args)
1070
+ {
1071
+ v8::Isolate* isolate = args.GetIsolate();
1072
+ auto& pipeline = Unwrap<wgpu::RenderPipeline>(args.This());
1073
+ uint32_t index = args.Length() > 0 && args[0]->IsNumber()
1074
+ ? static_cast<uint32_t>(args[0].As<v8::Number>()->Value()) : 0;
1075
+ args.GetReturnValue().Set(
1076
+ WrapWith(isolate, Tmpl(isolate).bindGroupLayout, pipeline.GetBindGroupLayout(index)));
1077
+ }
1078
+
1079
+ // ---------------------------------------------------------------------------
1080
+ // メソッド付与済みラップ (前方宣言の定義)
1081
+ // ---------------------------------------------------------------------------
1082
+ static v8::Local<v8::Object> WrapBuffer(v8::Isolate* isolate, wgpu::Buffer buffer)
1083
+ {
1084
+ auto obj = WrapWith(isolate, Tmpl(isolate).buffer, std::move(buffer));
1085
+ SetValue(isolate, obj, "size", v8::Number::New(isolate,
1086
+ static_cast<double>(Unwrap<wgpu::Buffer>(obj).GetSize())));
1087
+ SetMethod(isolate, obj, "getMappedRange", Buffer_GetMappedRange);
1088
+ SetMethod(isolate, obj, "unmap", Buffer_Unmap);
1089
+ SetMethod(isolate, obj, "mapAsync", Buffer_MapAsync);
1090
+ SetMethod(isolate, obj, "destroy", Buffer_Destroy);
1091
+ return obj;
1092
+ }
1093
+
1094
+ static v8::Local<v8::Object> WrapTexture(v8::Isolate* isolate, wgpu::Texture texture)
1095
+ {
1096
+ auto obj = WrapWith(isolate, Tmpl(isolate).texture, std::move(texture));
1097
+ auto& t = Unwrap<wgpu::Texture>(obj);
1098
+ SetValue(isolate, obj, "width", v8::Integer::NewFromUnsigned(isolate, t.GetWidth()));
1099
+ SetValue(isolate, obj, "height", v8::Integer::NewFromUnsigned(isolate, t.GetHeight()));
1100
+ SetMethod(isolate, obj, "createView", Texture_CreateView);
1101
+ SetMethod(isolate, obj, "destroy", Texture_Destroy);
1102
+ return obj;
1103
+ }
1104
+
1105
+ static v8::Local<v8::Object> WrapEncoder(v8::Isolate* isolate, wgpu::CommandEncoder encoder)
1106
+ {
1107
+ auto obj = WrapWith(isolate, Tmpl(isolate).commandEncoder, std::move(encoder));
1108
+ SetMethod(isolate, obj, "beginRenderPass", Encoder_BeginRenderPass);
1109
+ SetMethod(isolate, obj, "finish", Encoder_Finish);
1110
+ SetMethod(isolate, obj, "copyBufferToBuffer", Encoder_CopyBufferToBuffer);
1111
+ SetMethod(isolate, obj, "copyTextureToTexture", Encoder_CopyTextureToTexture);
1112
+ SetMethod(isolate, obj, "copyTextureToBuffer", Encoder_CopyTextureToBuffer);
1113
+ return obj;
1114
+ }
1115
+
1116
+ static v8::Local<v8::Object> WrapPipeline(v8::Isolate* isolate, wgpu::RenderPipeline pipeline)
1117
+ {
1118
+ auto obj = WrapWith(isolate, Tmpl(isolate).renderPipeline, std::move(pipeline));
1119
+ SetMethod(isolate, obj, "getBindGroupLayout", Pipeline_GetBindGroupLayout);
1120
+ return obj;
1121
+ }
1122
+
1123
+ // ---------------------------------------------------------------------------
1124
+ // GPUDevice / GPUQueue / GPUAdapter のラップ
1125
+ // ---------------------------------------------------------------------------
1126
+ static v8::Local<v8::Object> WrapQueue(v8::Isolate* isolate, wgpu::Queue queue)
1127
+ {
1128
+ auto obj = WrapWith(isolate, Tmpl(isolate).queue, std::move(queue));
1129
+ SetMethod(isolate, obj, "submit", Queue_Submit);
1130
+ SetMethod(isolate, obj, "writeBuffer", Queue_WriteBuffer);
1131
+ SetMethod(isolate, obj, "writeTexture", Queue_WriteTexture);
1132
+ SetMethod(isolate, obj, "copyExternalImageToTexture", Queue_CopyExternalImageToTexture);
1133
+ return obj;
1134
+ }
1135
+
1136
+ static v8::Local<v8::Object> WrapDevice(v8::Isolate* isolate, wgpu::Device device)
1137
+ {
1138
+ auto obj = WrapWith(isolate, Tmpl(isolate).device, device);
1139
+ SetMethod(isolate, obj, "createBuffer", Device_CreateBuffer);
1140
+ SetMethod(isolate, obj, "createTexture", Device_CreateTexture);
1141
+ SetMethod(isolate, obj, "createSampler", Device_CreateSampler);
1142
+ SetMethod(isolate, obj, "createShaderModule", Device_CreateShaderModule);
1143
+ SetMethod(isolate, obj, "createBindGroupLayout", Device_CreateBindGroupLayout);
1144
+ SetMethod(isolate, obj, "createBindGroup", Device_CreateBindGroup);
1145
+ SetMethod(isolate, obj, "createPipelineLayout", Device_CreatePipelineLayout);
1146
+ SetMethod(isolate, obj, "createRenderPipeline", Device_CreateRenderPipeline);
1147
+ SetMethod(isolate, obj, "createCommandEncoder", Device_CreateCommandEncoder);
1148
+ SetValue(isolate, obj, "queue", WrapQueue(isolate, device.GetQueue()));
1149
+ SetValue(isolate, obj, "features", v8::Object::New(isolate));
1150
+
1151
+ // limits: player は maxTextureDimension2D で描画最大サイズを決めるため実値を返す。
1152
+ // GetLimits の戻り値型は Dawn バージョン差があるため値でガードする(0 なら保証最小値)。
1153
+ v8::Local<v8::Object> limits = v8::Object::New(isolate);
1154
+ wgpu::Limits l = {};
1155
+ device.GetLimits(&l);
1156
+ auto orDefault = [](uint32_t v, uint32_t fb) { return v ? v : fb; };
1157
+ SetValue(isolate, limits, "maxTextureDimension1D", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxTextureDimension1D, 8192)));
1158
+ SetValue(isolate, limits, "maxTextureDimension2D", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxTextureDimension2D, 8192)));
1159
+ SetValue(isolate, limits, "maxTextureDimension3D", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxTextureDimension3D, 2048)));
1160
+ SetValue(isolate, limits, "maxBindGroups", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxBindGroups, 4)));
1161
+ SetValue(isolate, limits, "maxBufferSize", v8::Number::New(isolate, static_cast<double>(l.maxBufferSize ? l.maxBufferSize : (256u << 20))));
1162
+ SetValue(isolate, limits, "maxVertexBuffers", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxVertexBuffers, 8)));
1163
+ SetValue(isolate, limits, "maxVertexAttributes", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxVertexAttributes, 16)));
1164
+ SetValue(isolate, limits, "maxColorAttachments", v8::Integer::NewFromUnsigned(isolate, orDefault(l.maxColorAttachments, 8)));
1165
+ SetValue(isolate, obj, "limits", limits);
1166
+ return obj;
1167
+ }
1168
+
1169
+ // requestDevice() -> Promise<GPUDevice>
1170
+ static void Adapter_RequestDevice(const v8::FunctionCallbackInfo<v8::Value>& args)
1171
+ {
1172
+ v8::Isolate* isolate = args.GetIsolate();
1173
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
1174
+ DawnContext* gpu = HostContext::From(isolate)->gpu;
1175
+
1176
+ auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
1177
+ args.GetReturnValue().Set(resolver->GetPromise());
1178
+
1179
+ // DawnContext が単一 device を保持。取得済みならそれをラップする。
1180
+ if (!gpu->ready() && !gpu->AcquireDevice()) {
1181
+ resolver->Reject(ctx, v8::Exception::Error(Str(isolate, "requestDevice failed"))).Check();
1182
+ return;
1183
+ }
1184
+ resolver->Resolve(ctx, WrapDevice(isolate, gpu->device())).Check();
1185
+ }
1186
+
1187
+ // navigator.gpu.requestAdapter() -> Promise<GPUAdapter>
1188
+ static void GPU_RequestAdapter(const v8::FunctionCallbackInfo<v8::Value>& args)
1189
+ {
1190
+ v8::Isolate* isolate = args.GetIsolate();
1191
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
1192
+ DawnContext* gpu = HostContext::From(isolate)->gpu;
1193
+
1194
+ auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
1195
+ args.GetReturnValue().Set(resolver->GetPromise());
1196
+
1197
+ if (!gpu->ready() && !gpu->AcquireDevice()) {
1198
+ resolver->Resolve(ctx, v8::Null(isolate)).Check();
1199
+ return;
1200
+ }
1201
+
1202
+ auto adapter = WrapWith(isolate, Tmpl(isolate).adapter, gpu->adapter());
1203
+ SetMethod(isolate, adapter, "requestDevice", Adapter_RequestDevice);
1204
+ SetValue(isolate, adapter, "features", v8::Object::New(isolate));
1205
+ SetValue(isolate, adapter, "limits", v8::Object::New(isolate));
1206
+ resolver->Resolve(ctx, adapter).Check();
1207
+ }
1208
+
1209
+ static void GPU_GetPreferredCanvasFormat(const v8::FunctionCallbackInfo<v8::Value>& args)
1210
+ {
1211
+ Ctx_GetConfiguredFormat(args);
1212
+ }
1213
+
1214
+ // ---------------------------------------------------------------------------
1215
+ // テンプレート初期化 + 定数 + navigator.gpu の設置
1216
+ // ---------------------------------------------------------------------------
1217
+ static void InitTemplates(v8::Isolate* isolate)
1218
+ {
1219
+ Templates& t = Tmpl(isolate);
1220
+ auto make = [&](v8::Global<v8::ObjectTemplate>& g) {
1221
+ g.Reset(isolate, HandleTemplate(isolate));
1222
+ };
1223
+ make(t.adapter); make(t.device); make(t.queue); make(t.buffer); make(t.texture);
1224
+ make(t.textureView); make(t.sampler); make(t.shaderModule); make(t.bindGroupLayout);
1225
+ make(t.bindGroup); make(t.pipelineLayout); make(t.renderPipeline); make(t.commandEncoder);
1226
+ make(t.renderPass); make(t.commandBuffer); make(t.canvasContext);
1227
+ }
1228
+
1229
+ static void SetFlag(v8::Isolate* isolate, v8::Local<v8::Object> obj, const char* k, uint32_t v)
1230
+ {
1231
+ SetValue(isolate, obj, k, v8::Integer::NewFromUnsigned(isolate, v));
1232
+ }
1233
+
1234
+ } // namespace webgpu
1235
+
1236
+ // Bindings.h から呼ばれる公開関数
1237
+ void InstallWebGPU(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host)
1238
+ {
1239
+ using namespace next2d::webgpu;
1240
+ v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
1241
+
1242
+ InitTemplates(isolate);
1243
+
1244
+ // navigator.gpu は WebGPU バックエンド時のみ公開する。
1245
+ // WebGL2 バックエンド(Switch 等)では navigator.gpu を出さず、player を
1246
+ // canvas.getContext('webgl2') 経路へフォールバックさせる。
1247
+ if (host->backend == GraphicsBackend::WebGPU) {
1248
+ // navigator は DomShims が生成する。存在しなければ作る。
1249
+ v8::Local<v8::Value> nav_val;
1250
+ v8::Local<v8::Object> navigator;
1251
+ if (global->Get(ctx, Str(isolate, "navigator")).ToLocal(&nav_val) && nav_val->IsObject()) {
1252
+ navigator = nav_val.As<v8::Object>();
1253
+ } else {
1254
+ navigator = v8::Object::New(isolate);
1255
+ SetValue(isolate, global, "navigator", navigator);
1256
+ }
1257
+
1258
+ v8::Local<v8::Object> gpu = v8::Object::New(isolate);
1259
+ SetMethod(isolate, gpu, "requestAdapter", GPU_RequestAdapter);
1260
+ SetMethod(isolate, gpu, "getPreferredCanvasFormat", GPU_GetPreferredCanvasFormat);
1261
+ SetValue(isolate, navigator, "gpu", gpu);
1262
+ }
1263
+
1264
+ // 使用フラグ定数群 (ブラウザが提供する GPU*Usage 等)
1265
+ auto usage = v8::Object::New(isolate);
1266
+ SetFlag(isolate, usage, "MAP_READ", 0x0001);
1267
+ SetFlag(isolate, usage, "MAP_WRITE", 0x0002);
1268
+ SetFlag(isolate, usage, "COPY_SRC", 0x0004);
1269
+ SetFlag(isolate, usage, "COPY_DST", 0x0008);
1270
+ SetFlag(isolate, usage, "INDEX", 0x0010);
1271
+ SetFlag(isolate, usage, "VERTEX", 0x0020);
1272
+ SetFlag(isolate, usage, "UNIFORM", 0x0040);
1273
+ SetFlag(isolate, usage, "STORAGE", 0x0080);
1274
+ SetFlag(isolate, usage, "INDIRECT", 0x0100);
1275
+ SetFlag(isolate, usage, "QUERY_RESOLVE", 0x0200);
1276
+ SetValue(isolate, global, "GPUBufferUsage", usage);
1277
+
1278
+ auto tex_usage = v8::Object::New(isolate);
1279
+ SetFlag(isolate, tex_usage, "COPY_SRC", 0x01);
1280
+ SetFlag(isolate, tex_usage, "COPY_DST", 0x02);
1281
+ SetFlag(isolate, tex_usage, "TEXTURE_BINDING", 0x04);
1282
+ SetFlag(isolate, tex_usage, "STORAGE_BINDING", 0x08);
1283
+ SetFlag(isolate, tex_usage, "RENDER_ATTACHMENT", 0x10);
1284
+ SetValue(isolate, global, "GPUTextureUsage", tex_usage);
1285
+
1286
+ auto stage = v8::Object::New(isolate);
1287
+ SetFlag(isolate, stage, "VERTEX", 0x1);
1288
+ SetFlag(isolate, stage, "FRAGMENT", 0x2);
1289
+ SetFlag(isolate, stage, "COMPUTE", 0x4);
1290
+ SetValue(isolate, global, "GPUShaderStage", stage);
1291
+
1292
+ auto color_write = v8::Object::New(isolate);
1293
+ SetFlag(isolate, color_write, "RED", 0x1);
1294
+ SetFlag(isolate, color_write, "GREEN", 0x2);
1295
+ SetFlag(isolate, color_write, "BLUE", 0x4);
1296
+ SetFlag(isolate, color_write, "ALPHA", 0x8);
1297
+ SetFlag(isolate, color_write, "ALL", 0xF);
1298
+ SetValue(isolate, global, "GPUColorWrite", color_write);
1299
+
1300
+ auto map_mode = v8::Object::New(isolate);
1301
+ SetFlag(isolate, map_mode, "READ", 0x1);
1302
+ SetFlag(isolate, map_mode, "WRITE", 0x2);
1303
+ SetValue(isolate, global, "GPUMapMode", map_mode);
1304
+ }
1305
+
1306
+ // Canvas.cpp から呼ばれる公開関数: canvas.getContext('webgpu') が返す
1307
+ // GPUCanvasContext を生成する (DawnContext のサーフェスを参照)。
1308
+ v8::Local<v8::Object> CreateWebGPUCanvasContext(v8::Isolate* isolate, HostContext* /*host*/)
1309
+ {
1310
+ using namespace next2d::webgpu;
1311
+ v8::Local<v8::Object> context = v8::Object::New(isolate);
1312
+ SetMethod(isolate, context, "configure", Ctx_Configure);
1313
+ SetMethod(isolate, context, "unconfigure", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
1314
+ SetMethod(isolate, context, "getCurrentTexture", Ctx_GetCurrentTexture);
1315
+ return context;
1316
+ }
1317
+
1318
+ } // namespace next2d