Skip to content

fix: respect source.origin in copyExternalImageToTexture - #435

Open
Josema wants to merge 1 commit into
wcandillon:mainfrom
Josema:codex/fix-image-bitmap-origin
Open

fix: respect source.origin in copyExternalImageToTexture#435
Josema wants to merge 1 commit into
wcandillon:mainfrom
Josema:codex/fix-image-bitmap-origin

Conversation

@Josema

@Josema Josema commented Jul 27, 2026

Copy link
Copy Markdown

Problem

The source descriptor already parsed and stored origin, but copyExternalImageToTexture() always uploaded pixels starting at the beginning of the ImageBitmap backing store.

For example, given this 2×2 image:

A B
C D

Copying the right column:

device.queue.copyExternalImageToTexture(
  {
    source: bitmap,
    origin: [1, 0],
  },
  { texture },
  {
    width: 1,
    height: 2,
  },
);

should upload:

B
D

Instead, it uploaded:

A
C

This is especially visible when using texture atlases. Copies intended to generate padding from the last row or column could read pixels from the beginning of the image, producing a solid line along the right or bottom edge.

The existing flipY path had a related issue: it flipped the complete bitmap before applying the copy extent. For a subregion, WebGPU expects only the selected region to be flipped.

Solution

The fix is intentionally limited to the existing copyExternalImageToTexture() implementation:

  • Read source.origin, defaulting to [0, 0].
  • Validate that origin + copySize fits inside the source bitmap.
  • Use the staging path when the origin is non-zero, or when flipY or alpha
    conversion is required.
  • Copy only the selected rows and columns into the staging buffer.
  • Reverse the selected rows while copying when flipY is enabled.
  • Apply alpha conversion to the staged subregion when required.
  • Upload the staging buffer with row layout matching the subregion.

The existing direct upload path remains unchanged for zero-origin copies that
do not require flipY or alpha conversion.

Validation

The regression suite covers:

  • A non-zero horizontal origin.
  • A non-zero vertical origin.
  • source.origin combined with alpha conversion.
  • source.origin combined with flipY.
  • Horizontal and vertical out-of-bounds source regions.
  • The existing zero-origin path.

Verified with:

yarn workspace react-native-webgpu test:ref \
  --runTestsByPath src/__tests__/ImageBitmapOrigin.spec.ts

yarn workspace react-native-webgpu test:node \
  --runTestsByPath src/__tests__/ImageBitmapOrigin.spec.ts

yarn workspace react-native-webgpu test \
  --runTestsByPath src/__tests__/ImageBitmapOrigin.spec.ts

Results:

  • Chrome reference client: 7/7 passing.
  • dawn.node: 7/7 passing.
  • iOS Simulator (iPhone 11, iOS 26.2): 7/7 passing.
  • TypeScript and ESLint checks passing.
  • Native C++ source compiled successfully.

@wcandillon

Copy link
Copy Markdown
Owner

@Josema I see it marked as draft, let me know once it is ready for review 👍

@Josema
Josema marked this pull request as ready for review July 27, 2026 10:39
@Josema

Josema commented Jul 27, 2026

Copy link
Copy Markdown
Author

Ready. Again, this is a fix from Codex that works for me. Feel free to close the PR if you don't think the fix is good enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants