Skip to content

feat(texture): add 2D texture array creation and image-source population#422

Open
ryantrem wants to merge 4 commits into
masterfrom
add-texture-2d-arrays
Open

feat(texture): add 2D texture array creation and image-source population#422
ryantrem wants to merge 4 commits into
masterfrom
add-texture-2d-arrays

Conversation

@ryantrem

Copy link
Copy Markdown
Member

Summary

Adds a tree-shakable convenience layer for 2D texture arrays in Babylon Lite — the missing feature called out in the forum for populating a texture_2d_array directly from image assets (ImageBitmap, ImageData, canvas, video, or URL), without the "draw to an offscreen canvas and read back raw bytes" workaround.

Forum context:

What''s added

New free functions in packages/babylon-lite/src/texture/texture-array.ts:

  • createTexture2DArray(engine, width, height, layers, options?) — allocate an empty N-layer RGBA8 array.
  • uploadImageToArrayLayer(engine, tex, layer, source, opts?) — fill one layer from any WebGPU external-image source via a single copyExternalImageToTexture.
  • loadImageToArrayLayer(engine, tex, layer, url, opts?) — fetch + decode a URL into a layer.
  • createTexture2DArrayFromUrls(engine, urls, options?) — build a full array from image URLs. urls is a tuple readonly [string, ...string[]], so at-least-one-URL is enforced at compile time with zero runtime cost.

The returned Texture2DArray extends Texture2D, so it drops straight into existing texture bindings.

Tree-shaking

The whole feature is free functions with zero module-level side effects — an app that never touches texture arrays strips it entirely, and one that already holds an ImageBitmap never bundles the URL-fetch path.

Consuming a texture array

There is no built-in material that samples an array layer, so consumption goes through a custom shader. The texture-array.ts header doc includes a full @example: build the array, declare a viewDimension: "2d-array" sampler on a ShaderMaterial, and sample a chosen layer in WGSL via textureSample(atlas, atlasSampler, uv, i32(shaderUniforms.layer)).

Testing

  • Adds tests/lite/unit/texture-array.test.ts (creation, per-layer upload, URL loading, dimension validation, compile-time non-empty tuple guard).
  • Exports the new functions from packages/babylon-lite/src/index.ts.

All agent-allowed tests (build + parity) and lint pass. No bundle-size ceiling, golden-reference, or MAD-parity changes.

ryantrem and others added 3 commits July 15, 2026 13:49
Adds a tree-shakable, zero-side-effect texture-array module — the Lite
analog of BJS RawTexture2DArray, with the image-population path both
engines lack. Exposes createTexture2DArray, uploadImageToArrayLayer,
loadImageToArrayLayer, and createTexture2DArrayFromUrls, backed by
WebGPU copyExternalImageToTexture (per-layer origin), so ImageBitmap,
ImageData, canvas, and video sources all work with no branching.

Covered by 12 unit tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ace1149-800d-4bb4-a84d-15780ea0a0d5
Document that consuming a Texture2DArray requires a custom shader and show
the full path: build the array, declare a 2d-array sampler on a ShaderMaterial,
and sample a chosen layer in WGSL.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ace1149-800d-4bb4-a84d-15780ea0a0d5
Copilot AI review requested due to automatic review settings July 16, 2026 23:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@bjsplat

bjsplat commented Jul 16, 2026

Copy link
Copy Markdown

API Changes

API Extractor detected public API changes for @babylonjs/lite.

No removed public API lines were detected; this appears to be additive.

API Extractor diff
diff --git a/home/vsts/work/1/s/test-results/api-report/target/temp/babylon-lite.api.md b/home/vsts/work/1/s/test-results/api-report/current/temp/babylon-lite.api.md
index 0910ad69..76e4b7cd 100644
--- a/home/vsts/work/1/s/test-results/api-report/target/temp/babylon-lite.api.md
+++ b/home/vsts/work/1/s/test-results/api-report/current/temp/babylon-lite.api.md
@@ -365,6 +365,12 @@ export interface ArcRotateInterpolationOptions {
     interpolationFactor?: number;
 }
 
+// @public
+export interface ArrayLayerUploadOptions {
+    invertY?: boolean;
+    premultiplyAlpha?: boolean;
+}
+
 // @public
 export interface AssetContainer {
     animationGroups?: AnimationGroup[];
@@ -1607,6 +1613,12 @@ export function createTextRenderable(data: TextData, options?: TextRenderableOpt
 // @public
 export function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer;
 
+// @public
+export function createTexture2DArray(engine: EngineContext, width: number, height: number, layers: number, options?: TextureArrayOptions): Texture2DArray;
+
+// @public
+export function createTexture2DArrayFromUrls(engine: EngineContext, urls: readonly [string, ...string[]], options?: TextureArrayOptions): Promise<Texture2DArray>;
+
 // @public
 export function createTexture2DFromPixels(engine: EngineContext, data: Uint8Array, width: number, height: number, options?: PixelsTexture2DOptions): Texture2D;
 
@@ -2989,6 +3001,9 @@ export function loadGltf(engine: EngineContext, source: string | ArrayBuffer | B
 // @public
 export function loadHdrEnvironment(scene: SceneContext, url: string, options?: HdrLoadOptions): Promise<EnvironmentTextures>;
 
+// @public
+export function loadImageToArrayLayer(engine: EngineContext, tex: Texture2DArray, layer: number, url: string, opts?: ArrayLayerUploadOptions): Promise<void>;
+
 // @public
 export function loadKtx2Texture2D(engine: EngineContext, url: string, sRGB?: boolean): Promise<Texture2D>;
 
@@ -5996,6 +6011,12 @@ export interface Texture2D {
     width: number;
 }
 
+// @public
+export interface Texture2DArray extends Texture2D {
+    // (undocumented)
+    layers: number;
+}
+
 // @public
 export interface Texture2DOptions {
     addressModeU?: GPUAddressMode;
@@ -6013,6 +6034,16 @@ export type Texture3D = Texture2D & {
     depth: number;
 };
 
+// @public
+export interface TextureArrayOptions {
+    addressModeU?: GPUAddressMode;
+    addressModeV?: GPUAddressMode;
+    magFilter?: GPUFilterMode;
+    minFilter?: GPUFilterMode;
+    mipMaps?: boolean;
+    srgb?: boolean;
+}
+
 // @public
 export interface ThicknessProps {
     max?: number;
@@ -6262,6 +6293,9 @@ export function updateTextData(data: TextData, update: TextDataUpdate): void;
 // @public
 export function updateTexture2DFromPixels(engine: EngineContext, tex: Texture2D, data: Uint8Array, x?: number, y?: number, width?: number, height?: number): void;
 
+// @public
+export function uploadImageToArrayLayer(engine: EngineContext, tex: Texture2DArray, layer: number, source: GPUCopyExternalImageSource, opts?: ArrayLayerUploadOptions): void;
+
 // @public
 export interface UtilityLayer {
     // (undocumented)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1ace1149-800d-4bb4-a84d-15780ea0a0d5
@bjsplat

bjsplat commented Jul 16, 2026

Copy link
Copy Markdown

API Changes

API Extractor detected public API changes for @babylonjs/lite.

No removed public API lines were detected; this appears to be additive.

API Extractor diff
diff --git a/home/vsts/work/1/s/test-results/api-report/target/temp/babylon-lite.api.md b/home/vsts/work/1/s/test-results/api-report/current/temp/babylon-lite.api.md
index 0910ad69..76e4b7cd 100644
--- a/home/vsts/work/1/s/test-results/api-report/target/temp/babylon-lite.api.md
+++ b/home/vsts/work/1/s/test-results/api-report/current/temp/babylon-lite.api.md
@@ -365,6 +365,12 @@ export interface ArcRotateInterpolationOptions {
     interpolationFactor?: number;
 }
 
+// @public
+export interface ArrayLayerUploadOptions {
+    invertY?: boolean;
+    premultiplyAlpha?: boolean;
+}
+
 // @public
 export interface AssetContainer {
     animationGroups?: AnimationGroup[];
@@ -1607,6 +1613,12 @@ export function createTextRenderable(data: TextData, options?: TextRenderableOpt
 // @public
 export function createTextRenderer(surface: SurfaceContext, opts: TextRendererOptions): TextRenderer;
 
+// @public
+export function createTexture2DArray(engine: EngineContext, width: number, height: number, layers: number, options?: TextureArrayOptions): Texture2DArray;
+
+// @public
+export function createTexture2DArrayFromUrls(engine: EngineContext, urls: readonly [string, ...string[]], options?: TextureArrayOptions): Promise<Texture2DArray>;
+
 // @public
 export function createTexture2DFromPixels(engine: EngineContext, data: Uint8Array, width: number, height: number, options?: PixelsTexture2DOptions): Texture2D;
 
@@ -2989,6 +3001,9 @@ export function loadGltf(engine: EngineContext, source: string | ArrayBuffer | B
 // @public
 export function loadHdrEnvironment(scene: SceneContext, url: string, options?: HdrLoadOptions): Promise<EnvironmentTextures>;
 
+// @public
+export function loadImageToArrayLayer(engine: EngineContext, tex: Texture2DArray, layer: number, url: string, opts?: ArrayLayerUploadOptions): Promise<void>;
+
 // @public
 export function loadKtx2Texture2D(engine: EngineContext, url: string, sRGB?: boolean): Promise<Texture2D>;
 
@@ -5996,6 +6011,12 @@ export interface Texture2D {
     width: number;
 }
 
+// @public
+export interface Texture2DArray extends Texture2D {
+    // (undocumented)
+    layers: number;
+}
+
 // @public
 export interface Texture2DOptions {
     addressModeU?: GPUAddressMode;
@@ -6013,6 +6034,16 @@ export type Texture3D = Texture2D & {
     depth: number;
 };
 
+// @public
+export interface TextureArrayOptions {
+    addressModeU?: GPUAddressMode;
+    addressModeV?: GPUAddressMode;
+    magFilter?: GPUFilterMode;
+    minFilter?: GPUFilterMode;
+    mipMaps?: boolean;
+    srgb?: boolean;
+}
+
 // @public
 export interface ThicknessProps {
     max?: number;
@@ -6262,6 +6293,9 @@ export function updateTextData(data: TextData, update: TextDataUpdate): void;
 // @public
 export function updateTexture2DFromPixels(engine: EngineContext, tex: Texture2D, data: Uint8Array, x?: number, y?: number, width?: number, height?: number): void;
 
+// @public
+export function uploadImageToArrayLayer(engine: EngineContext, tex: Texture2DArray, layer: number, source: GPUCopyExternalImageSource, opts?: ArrayLayerUploadOptions): void;
+
 // @public
 export interface UtilityLayer {
     // (undocumented)

@bjsplat

bjsplat commented Jul 16, 2026

Copy link
Copy Markdown

Lite Playground - Static Site

Open deployed site

Build 20260716.11 - merge @ a8a2d0b

@bjsplat

bjsplat commented Jul 17, 2026

Copy link
Copy Markdown

Lab - Static Site

Open deployed site

Build 20260716.11 - merge @ a8a2d0b

@ryantrem
ryantrem enabled auto-merge (squash) July 17, 2026 01:57
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.

3 participants