Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next2d/player",
"version": "3.0.2",
"version": "3.0.3",
"description": "Experience the fast and beautiful anti-aliased rendering of WebGL. You can create rich, interactive graphics, cross-platform applications and games without worrying about browser or device compatibility.",
"author": "Toshiyuki Ienaga<ienaga@next2d.app> (https://github.com/ienaga/)",
"license": "MIT",
Expand Down
52 changes: 31 additions & 21 deletions packages/webgpu/src/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2746,10 +2746,11 @@ export class Context
*/
async createImageBitmap (width: number, height: number): Promise<ImageBitmap>
{
// アトラステクスチャから現在の描画内容を取得
const attachment = $getAtlasAttachmentObject();
if (!attachment) {
throw new Error("[WebGPU] Atlas attachment not found");
// メインアタッチメントから合成済み描画結果を取得
// (drawArraysInstanced()がアトラスからメインアタッチメントへ合成済み)
const mainAttachment = this.$mainAttachmentObject;
if (!mainAttachment || !mainAttachment.texture) {
throw new Error("[WebGPU] Main attachment not found");
}

// 描画を完了
Expand All @@ -2769,17 +2770,13 @@ export class Context
"usage": GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
});

// コマンドエンコーダーを作成
const commandEncoder = this.device.createCommandEncoder();

// アトラステクスチャからピクセルバッファにコピー
if (!attachment.texture) {
throw new Error("Attachment texture is null");
}
// フレームのcommandEncoderにcopyコマンドを追加
// 描画コマンドの後にコピーが実行されるため、正しい順序が保証される
this.ensureCommandEncoder();

commandEncoder.copyTextureToBuffer(
this.commandEncoder!.copyTextureToBuffer(
{
"texture": attachment.texture.resource,
"texture": mainAttachment.texture.resource,
"mipLevel": 0,
"origin": { "x": 0, "y": 0, "z": 0 }
},
Expand All @@ -2795,23 +2792,36 @@ export class Context
}
);

// コマンドを送信
this.device.queue.submit([commandEncoder.finish()]);
// endFrame()で描画コマンドとcopyコマンドを一括submit
// swap chain textureの参照もクリアされる
this.endFrame();
Comment on lines 2773 to 2797
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createImageBitmap() now relies on endFrame() to submit the copyTextureToBuffer command, but endFrame() is a no-op when frameStarted is false. If this API is called before beginFrame()/drawArraysInstanced(), the copy will never be submitted and mapAsync() can hang. Consider ensuring the frame is started here (e.g., call beginFrame() when needed / set frameStarted) or submit a dedicated command buffer for the copy like the previous implementation, instead of depending on endFrame().

Copilot uses AI. Check for mistakes.

// バッファをマップして読み込み
await pixelBuffer.mapAsync(GPUMapMode.READ);
const mappedRange = pixelBuffer.getMappedRange();
const pixels = new Uint8Array(mappedRange);

// ピクセルデータをコピー(アライメントを考慮)
// メインアタッチメントはbgra8unormフォーマットのため、
// ImageData(RGBA)用にB⇔Rチャンネルをスワップしつつコピー
Comment on lines +2804 to +2805
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says the main attachment is bgra8unorm, but the code already supports other formats via preferredFormat. To avoid misleading future maintainers (and accidental regressions if preferredFormat is rgba8unorm), update the comment to describe the conditional swap (e.g., only swap channels when preferredFormat is BGRA).

Suggested change
// メインアタッチメントはbgra8unormフォーマットのため
// ImageData(RGBA)用にB⇔Rチャンネルをスワップしつつコピー
// メインアタッチメントのフォーマットが bgra8unorm の場合は
// ImageData(RGBA)用に B⇔R チャンネルをスワップしつつコピー(それ以外のフォーマットはそのままコピー)

Copilot uses AI. Check for mistakes.
const isBgra = this.preferredFormat === "bgra8unorm";
const resultPixels = new Uint8Array(width * height * 4);
const rowBytes = width * 4;
for (let y = 0; y < height; y++) {
const srcOffset = y * bytesPerRow;
const dstOffset = y * width * 4;
resultPixels.set(
pixels.subarray(srcOffset, srcOffset + width * 4),
dstOffset
);
const dstOffset = y * rowBytes;
if (isBgra) {
for (let x = 0; x < rowBytes; x += 4) {
resultPixels[dstOffset + x ] = pixels[srcOffset + x + 2]; // R ← B
resultPixels[dstOffset + x + 1] = pixels[srcOffset + x + 1]; // G
resultPixels[dstOffset + x + 2] = pixels[srcOffset + x ]; // B ← R
resultPixels[dstOffset + x + 3] = pixels[srcOffset + x + 3]; // A
}
} else {
resultPixels.set(
pixels.subarray(srcOffset, srcOffset + rowBytes),
dstOffset
);
}
Comment on lines 2747 to 2824
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change alters capture output semantics (source attachment + color channel order) and introduces new ordering/submission behavior via ensureCommandEncoder()+endFrame(). Context.test.ts exists but doesn't cover createImageBitmap(); adding a unit test that validates correct RGBA output (especially on BGRA preferredFormat) and that the copy command gets submitted would help prevent regressions.

Copilot uses AI. Check for mistakes.
}

pixelBuffer.unmap();
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Next2D } from "@next2d/core";

if (!("next2d" in window)) {
console.log("%c Next2D Player %c 3.0.2 %c https://next2d.app",
console.log("%c Next2D Player %c 3.0.3 %c https://next2d.app",
"color: #fff; background: #5f5f5f",
"color: #fff; background: #4bc729",
"");
Expand Down
Loading