Conversation
There was a problem hiding this comment.
Pull request overview
WebGPU 環境での captureToCanvas の不具合修正を目的とし、キャプチャ用の読み戻し元を適切なアタッチメントに変更しつつ、プレイヤーバージョンを 3.0.3 に更新するPRです。
Changes:
- WebGPU の
Context.createImageBitmap()を「アトラス」ではなく「メインアタッチメント」から読み戻すように変更 copyTextureToBufferをフレームのcommandEncoderに追加し、endFrame()でまとめて submit する方式に変更- パッケージ/表示バージョンを 3.0.2 → 3.0.3 に更新
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/index.ts | 起動ログのバージョン表記を 3.0.3 に更新 |
| packages/webgpu/src/Context.ts | キャプチャの読み戻し元・submit順序・BGRA→RGBA 変換処理を更新 |
| package.json | パッケージバージョンを 3.0.3 に更新 |
| package-lock.json | ロックファイル内バージョンを 3.0.3 に更新 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -2795,23 +2792,36 @@ export class Context | |||
| } | |||
| ); | |||
|
|
|||
| // コマンドを送信 | |||
| this.device.queue.submit([commandEncoder.finish()]); | |||
| // endFrame()で描画コマンドとcopyコマンドを一括submit | |||
| // swap chain textureの参照もクリアされる | |||
| this.endFrame(); | |||
There was a problem hiding this comment.
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().
| // メインアタッチメントはbgra8unormフォーマットのため、 | ||
| // ImageData(RGBA)用にB⇔Rチャンネルをスワップしつつコピー |
There was a problem hiding this comment.
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).
| // メインアタッチメントはbgra8unormフォーマットのため、 | |
| // ImageData(RGBA)用にB⇔Rチャンネルをスワップしつつコピー | |
| // メインアタッチメントのフォーマットが bgra8unorm の場合は、 | |
| // ImageData(RGBA)用に B⇔R チャンネルをスワップしつつコピー(それ以外のフォーマットはそのままコピー) |
| @@ -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 } | |||
| }, | |||
| @@ -2795,23 +2792,36 @@ export class Context | |||
| } | |||
| ); | |||
|
|
|||
| // コマンドを送信 | |||
| this.device.queue.submit([commandEncoder.finish()]); | |||
| // endFrame()で描画コマンドとcopyコマンドを一括submit | |||
| // swap chain textureの参照もクリアされる | |||
| this.endFrame(); | |||
|
|
|||
| // バッファをマップして読み込み | |||
| await pixelBuffer.mapAsync(GPUMapMode.READ); | |||
| const mappedRange = pixelBuffer.getMappedRange(); | |||
| const pixels = new Uint8Array(mappedRange); | |||
|
|
|||
| // ピクセルデータをコピー(アライメントを考慮) | |||
| // メインアタッチメントはbgra8unormフォーマットのため、 | |||
| // ImageData(RGBA)用にB⇔Rチャンネルをスワップしつつコピー | |||
| 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 | |||
| ); | |||
| } | |||
There was a problem hiding this comment.
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.
No description provided.