Skip to content

Commit bd8c8b5

Browse files
committed
#75 wip: xboxビルドテスト v63
1 parent ce035ef commit bd8c8b5

9 files changed

Lines changed: 1076 additions & 254 deletions

File tree

templates/xbox/src/bindings/Canvas2D.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,22 @@ void Restore(const v8::FunctionCallbackInfo<v8::Value>& a)
268268
Canvas2D* c = Self(a.This());
269269
if (!c->stack.empty()) { c->state = c->stack.back(); c->stack.pop_back(); }
270270
}
271-
// clip(): 現在パスの外接矩形をクリップ領域として設定する (デバイス座標)。
272-
// 既存クリップとは積集合を取る。player の TextField 枠は矩形パスなので外接矩形で十分。
273-
// «EXTEND» 任意形状の厳密クリップは per-pixel マスクが必要 (現状は矩形近似)。
271+
// clip(): 現在パスをクリップ領域として設定する (デバイス座標)。既存クリップと積集合
272+
// 矩形パス (TextField 枠等) → 外接矩形クリップ (厳密・軽量)
273+
// 任意形状パス (円/多角形等) → nonzero winding のピクセルマスクで厳密にクリップ
274274
void Clip(const v8::FunctionCallbackInfo<v8::Value>& a)
275275
{
276276
Canvas2D* c = Self(a.This());
277277
double minx, miny, maxx, maxy;
278278
if (!raster::PathBounds(c->path, &minx, &miny, &maxx, &maxy)) return;
279-
raster::IntersectClip(c->state.clip,
280-
std::floor(minx), std::floor(miny), std::ceil(maxx), std::ceil(maxy));
279+
if (raster::IsAxisAlignedRect(c->path)) {
280+
// 矩形クリップは外接矩形で厳密 (軽量)。
281+
raster::IntersectClip(c->state.clip,
282+
std::floor(minx), std::floor(miny), std::ceil(maxx), std::ceil(maxy));
283+
} else {
284+
// 任意形状はピクセルマスクで厳密にクリップする (外接矩形も内部で反映)。
285+
raster::IntersectClipMask(c->state.clip, c->path, c->surface.width, c->surface.height);
286+
}
281287
}
282288

283289
// --- 矩形 / クリア --------------------------------------------------------

templates/xbox/src/bindings/DomShims.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,15 @@ void CryptoRandomUUID(const v8::FunctionCallbackInfo<v8::Value>& args)
151151
// --- 名前付きストレージの永続化バックエンド ---------------------------------
152152
// localStorage / indexedDB のセマンティクスは Polyfills.cpp の JS 側が実装し、
153153
// ここは名前ごとのファイル I/O のみを担う。
154-
// 保存先: %LOCALAPPDATA%\Next2D\<name>.json
155-
// «EXTEND» コンソール実機では XGameSave への置き換えが必要 (devkit 後の作業)。
154+
// 保存先: %LOCALAPPDATA%\Next2D\<name>.json (PC / Gaming.Desktop / CI)。
155+
//
156+
// コンソール実機 (Gaming.Xbox.*) のストレージ対応 (要 devkit 検証):
157+
// - localStorage / indexedDB のようなタイトルローカル永続化は XPersistentLocalStorage
158+
// (XPersistentLocalStorageGetPath で得たパス配下へ同じファイル I/O) が対応先。
159+
// 同期 API のため現行の同期ストレージ契約 (StorageLoad/StorageSave) にそのまま載る。
160+
// - ユーザー毎のクラウド同期セーブが要る場合のみ XGameSave (非同期・XUser 必須) を使う。
161+
// いずれも console GDK ヘッダと signed-in user / SCID 構成が必要で、devkit 実機でしか
162+
// コンパイル・実行検証できないため未実装 (現状はファイルパスのフォールバックで動作)。
156163
std::wstring StorageFilePath(const std::string& name)
157164
{
158165
// ファイル名として安全な文字だけを残す

templates/xbox/src/bindings/Polyfills.cpp

Lines changed: 671 additions & 236 deletions
Large diffs are not rendered by default.

templates/xbox/src/bindings/RasterCore.h

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <cctype>
88
#include <cmath>
99
#include <cstdint>
10+
#include <memory>
1011
#include <string>
1112
#include <utility>
1213
#include <vector>
@@ -26,10 +27,18 @@ struct SubPath {
2627

2728
struct RGBA { uint8_t r=0,g=0,b=0,a=255; };
2829

29-
// クリップ矩形 (デバイス座標, [x0,x1) x [y0,y1))。active=false なら無制限。
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 を割り当てるので復元が壊れない)。
3037
struct ClipRect {
3138
bool active = false;
3239
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;
3342
};
3443

3544
// RGBA8 のピクセルバッファ。
@@ -101,6 +110,9 @@ inline void Blend(Surface& s, const ClipRect& clip, int x, int y, const RGBA& co
101110
{
102111
if (x < 0 || y < 0 || x >= s.width || y >= s.height) return;
103112
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;
104116
size_t i = (static_cast<size_t>(y) * s.width + x) * 4;
105117
double a = (col.a / 255.0) * alpha;
106118
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
215227
}
216228
}
217229

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+
218322
// 3次ベジェを現在サブパスへフラット化して追記する。p0 は変換済みの直前点。
219323
inline void FlattenCubic(std::vector<Point>& out,
220324
Point p0, Point p1, Point p2, Point p3, int steps = 16)

templates/xbox/src/bindings/webgpu/WebGPU.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1213,9 +1213,13 @@ static void Device_CreateBindGroupLayout(const v8::FunctionCallbackInfo<v8::Valu
12131213
v8::Local<v8::Object> d = args[0].As<v8::Object>();
12141214

12151215
std::vector<wgpu::BindGroupLayoutEntry> entries;
1216+
// externalTexture の nextInChain 構造体は CreateBindGroupLayout 完了まで生存が必要。
1217+
// reserve でベクタ再割当を防ぎ、back() のアドレスを安定させる。
1218+
std::vector<wgpu::ExternalTextureBindingLayout> ext_layouts;
12161219
v8::Local<v8::Value> ev = Prop(isolate, d, "entries");
12171220
if (ev->IsArray()) {
12181221
auto arr = ev.As<v8::Array>();
1222+
ext_layouts.reserve(arr->Length());
12191223
for (uint32_t i = 0; i < arr->Length(); ++i) {
12201224
v8::Local<v8::Value> v;
12211225
if (!arr->Get(ctx, i).ToLocal(&v) || !v->IsObject()) continue;
@@ -1239,8 +1243,21 @@ static void Device_CreateBindGroupLayout(const v8::FunctionCallbackInfo<v8::Valu
12391243
? ToTextureViewDimension(webgpu::Str(isolate, t, "viewDimension"))
12401244
: wgpu::TextureViewDimension::e2D;
12411245
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();
12421260
}
1243-
// «EXTEND» storageTexture / externalTexture は必要に応じて追加
12441261
entries.push_back(entry);
12451262
}
12461263
}

templates/xbox/src/bindings/webgpu/WebGPUEnums.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,54 @@ inline wgpu::TextureFormat ToTextureFormat(std::string_view s)
1717
if (s == "bgra8unorm") return F::BGRA8Unorm;
1818
if (s == "bgra8unorm-srgb") return F::BGRA8UnormSrgb;
1919
if (s == "rgba8snorm") return F::RGBA8Snorm;
20+
if (s == "rgba8uint") return F::RGBA8Uint;
21+
if (s == "rgba8sint") return F::RGBA8Sint;
2022
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;
2126
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;
2232
if (s == "r16float") return F::R16Float;
33+
if (s == "rg16uint") return F::RG16Uint;
34+
if (s == "rg16sint") return F::RG16Sint;
2335
if (s == "rg16float") return F::RG16Float;
36+
if (s == "rgba16uint") return F::RGBA16Uint;
37+
if (s == "rgba16sint") return F::RGBA16Sint;
2438
if (s == "rgba16float") return F::RGBA16Float;
39+
if (s == "r32uint") return F::R32Uint;
40+
if (s == "r32sint") return F::R32Sint;
2541
if (s == "r32float") return F::R32Float;
42+
if (s == "rg32uint") return F::RG32Uint;
43+
if (s == "rg32sint") return F::RG32Sint;
2644
if (s == "rg32float") return F::RG32Float;
45+
if (s == "rgba32uint") return F::RGBA32Uint;
46+
if (s == "rgba32sint") return F::RGBA32Sint;
2747
if (s == "rgba32float") return F::RGBA32Float;
48+
if (s == "rgb10a2unorm") return F::RGB10A2Unorm;
49+
if (s == "rg11b10ufloat") return F::RG11B10Ufloat;
2850
if (s == "depth16unorm") return F::Depth16Unorm;
2951
if (s == "depth24plus") return F::Depth24Plus;
3052
if (s == "depth24plus-stencil8") return F::Depth24PlusStencil8;
3153
if (s == "depth32float") return F::Depth32Float;
54+
if (s == "depth32float-stencil8") return F::Depth32FloatStencil8;
3255
if (s == "stencil8") return F::Stencil8;
3356
return F::RGBA8Unorm;
3457
}
3558

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+
3668
inline wgpu::VertexFormat ToVertexFormat(std::string_view s)
3769
{
3870
using V = wgpu::VertexFormat;

0 commit comments

Comments
 (0)