@yali-engine/ktx2 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/decode.d.ts +35 -0
- package/dist/decode.js +46 -0
- package/dist/decode.js.map +1 -0
- package/dist/formats.d.ts +39 -0
- package/dist/formats.js +51 -0
- package/dist/formats.js.map +1 -0
- package/dist/image.d.ts +34 -0
- package/dist/image.js +104 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/ktx2.d.ts +67 -0
- package/dist/ktx2.js +207 -0
- package/dist/ktx2.js.map +1 -0
- package/dist/loader.d.ts +47 -0
- package/dist/loader.js +65 -0
- package/dist/loader.js.map +1 -0
- package/package.json +26 -3
- package/README.md +0 -3
package/dist/decode.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Variant } from './formats.ts';
|
|
2
|
+
/** A texture ready for the GPU, as a decode worker hands it over (levels transferred, not copied). */
|
|
3
|
+
export interface DecodedTexture {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly format: GPUTextureFormat;
|
|
7
|
+
readonly family: Variant;
|
|
8
|
+
readonly srgb: boolean;
|
|
9
|
+
/** Texels per block edge (1 or 4) and bytes per block. */
|
|
10
|
+
readonly block: 1 | 4;
|
|
11
|
+
readonly blockBytes: number;
|
|
12
|
+
/** Level 0 (full size) first, down to 1×1. */
|
|
13
|
+
readonly levels: readonly Uint8Array[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The `ktx2` decoder, for `serveDecoders` (ADR-0060): parses a GPU-native KTX2 file and checks
|
|
17
|
+
* it is a square power-of-two 2D texture with its full mip chain, the shape the texture store's
|
|
18
|
+
* arrays take. Basis-compressed files are refused here (the `@yali-engine/basis` plugin registers a
|
|
19
|
+
* decoder that transcodes them first).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // decode.worker.ts
|
|
23
|
+
* serveDecoders({ ktx2: decodeKtx2 });
|
|
24
|
+
*/
|
|
25
|
+
export declare function decodeKtx2(bytes: Uint8Array): DecodedTexture;
|
|
26
|
+
/** Throws unless the texture is square, a power of two, with every level down to 1×1. */
|
|
27
|
+
export declare function checkShape(width: number, height: number, levels: number): void;
|
|
28
|
+
/**
|
|
29
|
+
* The path of the variant a device should load for a logical texture path:
|
|
30
|
+
* `rock.ktx2` → `rock.bc7.ktx2`. A path that already names a variant is returned as is.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* variantPath('textures/rock.ktx2', 'astc'); // 'textures/rock.astc.ktx2'
|
|
34
|
+
*/
|
|
35
|
+
export declare function variantPath(path: string, variant: Variant): string;
|
package/dist/decode.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
import { VARIANTS } from "./formats.js";
|
|
3
|
+
import { formatOf, parseKtx2 } from "./ktx2.js";
|
|
4
|
+
/**
|
|
5
|
+
* The `ktx2` decoder, for `serveDecoders` (ADR-0060): parses a GPU-native KTX2 file and checks
|
|
6
|
+
* it is a square power-of-two 2D texture with its full mip chain, the shape the texture store's
|
|
7
|
+
* arrays take. Basis-compressed files are refused here (the `@yali-engine/basis` plugin registers a
|
|
8
|
+
* decoder that transcodes them first).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // decode.worker.ts
|
|
12
|
+
* serveDecoders({ ktx2: decodeKtx2 });
|
|
13
|
+
*/
|
|
14
|
+
export function decodeKtx2(bytes) {
|
|
15
|
+
const k = parseKtx2(bytes);
|
|
16
|
+
const info = formatOf(k);
|
|
17
|
+
checkShape(k.width, k.height, k.levels.length);
|
|
18
|
+
return { width: k.width, height: k.height, format: info.gpu, family: info.family, srgb: info.srgb, block: info.block, blockBytes: info.bytes, levels: k.levels };
|
|
19
|
+
}
|
|
20
|
+
/** Throws unless the texture is square, a power of two, with every level down to 1×1. */
|
|
21
|
+
export function checkShape(width, height, levels) {
|
|
22
|
+
const pow2 = (n) => n > 0 && (n & (n - 1)) === 0;
|
|
23
|
+
if (width !== height || !pow2(width)) {
|
|
24
|
+
throw new YaliError('TEXTURE_SHAPE', `The texture is ${width}×${height}; texture arrays need square power-of-two sizes.`, 'Run `yali convert`, which resizes to a square power of two (ADR-0060).');
|
|
25
|
+
}
|
|
26
|
+
const full = Math.log2(width) + 1;
|
|
27
|
+
if (levels !== full) {
|
|
28
|
+
throw new YaliError('TEXTURE_MIPS', `The texture has ${levels} mip levels; ${width}×${width} needs all ${full} down to 1×1.`, 'Export with mipmaps (`yali convert` writes them; toktx needs --genmipmap).');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The path of the variant a device should load for a logical texture path:
|
|
33
|
+
* `rock.ktx2` → `rock.bc7.ktx2`. A path that already names a variant is returned as is.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* variantPath('textures/rock.ktx2', 'astc'); // 'textures/rock.astc.ktx2'
|
|
37
|
+
*/
|
|
38
|
+
export function variantPath(path, variant) {
|
|
39
|
+
const m = /^(.*?)(\.(bc7|astc|etc2|rgba8))?\.ktx2(\?.*)?$/.exec(path);
|
|
40
|
+
if (!m)
|
|
41
|
+
throw new YaliError('TEXTURE_PATH', `"${path}" is not a .ktx2 path.`, 'Name textures `<name>.ktx2`; `yali convert` writes `<name>.<variant>.ktx2` beside it.');
|
|
42
|
+
if (m[3] && VARIANTS.includes(m[3]))
|
|
43
|
+
return path;
|
|
44
|
+
return `${m[1]}.${variant}.ktx2${m[4] ?? ''}`;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=decode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode.js","sourceRoot":"","sources":["../src/decode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAAgB,QAAQ,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAgBhD;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzB,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACnK,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAc,EAAE,MAAc;IACtE,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC,eAAe,EAAE,kBAAkB,KAAK,IAAI,MAAM,kDAAkD,EAAE,wEAAwE,CAAC,CAAC;IACtM,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,cAAc,EAAE,mBAAmB,MAAM,gBAAgB,KAAK,IAAI,KAAK,cAAc,IAAI,eAAe,EAAE,4EAA4E,CAAC,CAAC;IAC9M,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAgB;IACxD,MAAM,CAAC,GAAG,gDAAgD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,cAAc,EAAE,IAAI,IAAI,wBAAwB,EAAE,uFAAuF,CAAC,CAAC;IACvK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAK,QAA8B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The KTX2 `vkFormat`s Yali loads, with their WebGPU format and block layout (ADR-0060).
|
|
3
|
+
* Block formats are 4×4 texels; `bytes` is per block, or per texel for uncompressed ones.
|
|
4
|
+
*/
|
|
5
|
+
export interface FormatInfo {
|
|
6
|
+
/** The WebGPU texture format. */
|
|
7
|
+
readonly gpu: GPUTextureFormat;
|
|
8
|
+
/** The device feature it needs, or null (always available). */
|
|
9
|
+
readonly feature: GPUFeatureName | null;
|
|
10
|
+
/** Texels per block edge (1 = uncompressed). */
|
|
11
|
+
readonly block: 1 | 4;
|
|
12
|
+
/** Bytes per block (per texel when `block` is 1). */
|
|
13
|
+
readonly bytes: number;
|
|
14
|
+
/** The family a variant file is named after (`foo.bc7.ktx2`). */
|
|
15
|
+
readonly family: Variant;
|
|
16
|
+
readonly srgb: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** The GPU-native variants `yali convert` writes (ADR-0060), in the default order of preference. */
|
|
19
|
+
export type Variant = 'bc7' | 'astc' | 'etc2' | 'rgba8';
|
|
20
|
+
/** The default preference: BC7 on desktop GPUs, then ASTC, then ETC2 (every GLES 3 device), then RGBA8. */
|
|
21
|
+
export declare const VARIANTS: readonly Variant[];
|
|
22
|
+
/** vkFormat → format. Only what Yali writes and loads; other formats are refused with a clear error. */
|
|
23
|
+
export declare const VK_FORMATS: ReadonlyMap<number, FormatInfo>;
|
|
24
|
+
/** vkFormat of each variant family, linear and sRGB (what `yali convert` writes for RGBA data). */
|
|
25
|
+
export declare const VARIANT_VK: Readonly<Record<Variant, {
|
|
26
|
+
linear: number;
|
|
27
|
+
srgb: number;
|
|
28
|
+
}>>;
|
|
29
|
+
/** The device feature a variant needs (null: always available). */
|
|
30
|
+
export declare const VARIANT_FEATURE: Readonly<Record<Variant, GPUFeatureName | null>>;
|
|
31
|
+
/**
|
|
32
|
+
* The first variant in `prefer` the device can sample, from its enabled features.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* pickVariant(new Set(['texture-compression-etc2'])); // 'etc2'
|
|
36
|
+
*/
|
|
37
|
+
export declare function pickVariant(features: ReadonlySet<string>, prefer?: readonly Variant[]): Variant;
|
|
38
|
+
/** Bytes of one mip level of `width` × `height` texels in `info`'s format. */
|
|
39
|
+
export declare function levelBytes(info: FormatInfo, width: number, height: number): number;
|
package/dist/formats.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** The default preference: BC7 on desktop GPUs, then ASTC, then ETC2 (every GLES 3 device), then RGBA8. */
|
|
2
|
+
export const VARIANTS = ['bc7', 'astc', 'etc2', 'rgba8'];
|
|
3
|
+
const f = (gpu, feature, block, bytes, family, srgb) => ({ gpu, feature, block, bytes, family, srgb });
|
|
4
|
+
/** vkFormat → format. Only what Yali writes and loads; other formats are refused with a clear error. */
|
|
5
|
+
export const VK_FORMATS = new Map([
|
|
6
|
+
[37, f('rgba8unorm', null, 1, 4, 'rgba8', false)],
|
|
7
|
+
[43, f('rgba8unorm-srgb', null, 1, 4, 'rgba8', true)],
|
|
8
|
+
[145, f('bc7-rgba-unorm', 'texture-compression-bc', 4, 16, 'bc7', false)],
|
|
9
|
+
[146, f('bc7-rgba-unorm-srgb', 'texture-compression-bc', 4, 16, 'bc7', true)],
|
|
10
|
+
[147, f('etc2-rgb8unorm', 'texture-compression-etc2', 4, 8, 'etc2', false)],
|
|
11
|
+
[148, f('etc2-rgb8unorm-srgb', 'texture-compression-etc2', 4, 8, 'etc2', true)],
|
|
12
|
+
[151, f('etc2-rgba8unorm', 'texture-compression-etc2', 4, 16, 'etc2', false)],
|
|
13
|
+
[152, f('etc2-rgba8unorm-srgb', 'texture-compression-etc2', 4, 16, 'etc2', true)],
|
|
14
|
+
[157, f('astc-4x4-unorm', 'texture-compression-astc', 4, 16, 'astc', false)],
|
|
15
|
+
[158, f('astc-4x4-unorm-srgb', 'texture-compression-astc', 4, 16, 'astc', true)],
|
|
16
|
+
]);
|
|
17
|
+
/** vkFormat of each variant family, linear and sRGB (what `yali convert` writes for RGBA data). */
|
|
18
|
+
export const VARIANT_VK = {
|
|
19
|
+
rgba8: { linear: 37, srgb: 43 },
|
|
20
|
+
bc7: { linear: 145, srgb: 146 },
|
|
21
|
+
etc2: { linear: 151, srgb: 152 },
|
|
22
|
+
astc: { linear: 157, srgb: 158 },
|
|
23
|
+
};
|
|
24
|
+
/** The device feature a variant needs (null: always available). */
|
|
25
|
+
export const VARIANT_FEATURE = {
|
|
26
|
+
bc7: 'texture-compression-bc',
|
|
27
|
+
astc: 'texture-compression-astc',
|
|
28
|
+
etc2: 'texture-compression-etc2',
|
|
29
|
+
rgba8: null,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The first variant in `prefer` the device can sample, from its enabled features.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* pickVariant(new Set(['texture-compression-etc2'])); // 'etc2'
|
|
36
|
+
*/
|
|
37
|
+
export function pickVariant(features, prefer = VARIANTS) {
|
|
38
|
+
for (const v of prefer) {
|
|
39
|
+
const need = VARIANT_FEATURE[v];
|
|
40
|
+
if (need === null || features.has(need))
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
return 'rgba8';
|
|
44
|
+
}
|
|
45
|
+
/** Bytes of one mip level of `width` × `height` texels in `info`'s format. */
|
|
46
|
+
export function levelBytes(info, width, height) {
|
|
47
|
+
const bw = Math.ceil(width / info.block);
|
|
48
|
+
const bh = Math.ceil(height / info.block);
|
|
49
|
+
return bw * bh * info.bytes;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=formats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.js","sourceRoot":"","sources":["../src/formats.ts"],"names":[],"mappings":"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"}
|
package/dist/image.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DecodedTexture } from './decode.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Every mip level of a square power-of-two RGBA8 image (level 0 = `rgba`), by 2×2 box
|
|
4
|
+
* filtering; colour channels of an sRGB image are averaged in linear light (alpha always
|
|
5
|
+
* linearly), so mips do not darken.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const levels = buildMips(rgba, 256, true); // 9 levels, 256² … 1²
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildMips(rgba: Uint8Array, size: number, srgb: boolean): Uint8Array[];
|
|
11
|
+
/** The power-of-two square an image of `w × h` is resampled to: its larger side rounded up, at most `maxSize`. */
|
|
12
|
+
export declare function squareSize(w: number, h: number, maxSize: number): number;
|
|
13
|
+
/** An RGBA8 texture from level-0 pixels (mips built here). */
|
|
14
|
+
export declare function rgbaTexture(rgba: Uint8Array, size: number, srgb: boolean): DecodedTexture;
|
|
15
|
+
/**
|
|
16
|
+
* Decodes a PNG, JPEG or WebP into RGBA8 pixels resampled to a power-of-two square of at most
|
|
17
|
+
* `maxSize` (UVs are normalized, so a non-square image keeps its mapping; ADR-0066). Needs
|
|
18
|
+
* `createImageBitmap` and `OffscreenCanvas` (browsers and their workers).
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeImagePixels(bytes: Uint8Array, maxSize: number): Promise<{
|
|
21
|
+
rgba: Uint8Array;
|
|
22
|
+
size: number;
|
|
23
|
+
}>;
|
|
24
|
+
/** A texture from a PNG, JPEG or WebP: decoded, resampled square, mipmapped (`decodeImagePixels` + `buildMips`). */
|
|
25
|
+
export declare function decodeImage(bytes: Uint8Array, options: {
|
|
26
|
+
srgb: boolean;
|
|
27
|
+
maxSize: number;
|
|
28
|
+
}): Promise<DecodedTexture>;
|
|
29
|
+
/**
|
|
30
|
+
* glTF keeps occlusion (R of its own texture) apart from metallic-roughness (G, B); the
|
|
31
|
+
* store's ORM slot wants both in one: R from `occlusion` (255 without), G and B from
|
|
32
|
+
* `metalRough` (255 without, so the factors apply unchanged). Inputs are square RGBA8 of `size`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function mergeOrm(occlusion: Uint8Array | null, metalRough: Uint8Array | null, size: number): Uint8Array;
|
package/dist/image.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
/** sRGB-encoded byte → linear 0–1, for averaging colour texels in linear light. */
|
|
3
|
+
const TO_LINEAR = Float32Array.from({ length: 256 }, (_, i) => {
|
|
4
|
+
const c = i / 255;
|
|
5
|
+
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
6
|
+
});
|
|
7
|
+
const toSrgbByte = (l) => {
|
|
8
|
+
const c = l <= 0.0031308 ? l * 12.92 : 1.055 * l ** (1 / 2.4) - 0.055;
|
|
9
|
+
return Math.round(Math.min(1, Math.max(0, c)) * 255);
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Every mip level of a square power-of-two RGBA8 image (level 0 = `rgba`), by 2×2 box
|
|
13
|
+
* filtering; colour channels of an sRGB image are averaged in linear light (alpha always
|
|
14
|
+
* linearly), so mips do not darken.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const levels = buildMips(rgba, 256, true); // 9 levels, 256² … 1²
|
|
18
|
+
*/
|
|
19
|
+
export function buildMips(rgba, size, srgb) {
|
|
20
|
+
if (size <= 0 || (size & (size - 1)) !== 0 || rgba.length !== size * size * 4) {
|
|
21
|
+
throw new YaliError('IMAGE_SHAPE', `buildMips needs a square power-of-two RGBA8 image; got ${rgba.length} bytes for ${size}².`, 'Resize to a power-of-two square first (decodeImage does).');
|
|
22
|
+
}
|
|
23
|
+
const levels = [rgba];
|
|
24
|
+
for (let s = size; s > 1; s >>= 1) {
|
|
25
|
+
const prev = levels[levels.length - 1];
|
|
26
|
+
const h = s >> 1;
|
|
27
|
+
const next = new Uint8Array(h * h * 4);
|
|
28
|
+
for (let y = 0; y < h; y++) {
|
|
29
|
+
for (let x = 0; x < h; x++) {
|
|
30
|
+
const a = ((2 * y) * s + 2 * x) * 4;
|
|
31
|
+
const b = a + 4;
|
|
32
|
+
const c = a + s * 4;
|
|
33
|
+
const d = c + 4;
|
|
34
|
+
const o = (y * h + x) * 4;
|
|
35
|
+
for (let k = 0; k < 3; k++) {
|
|
36
|
+
next[o + k] = srgb
|
|
37
|
+
? toSrgbByte((TO_LINEAR[prev[a + k]] + TO_LINEAR[prev[b + k]] + TO_LINEAR[prev[c + k]] + TO_LINEAR[prev[d + k]]) / 4)
|
|
38
|
+
: (prev[a + k] + prev[b + k] + prev[c + k] + prev[d + k] + 2) >> 2;
|
|
39
|
+
}
|
|
40
|
+
next[o + 3] = (prev[a + 3] + prev[b + 3] + prev[c + 3] + prev[d + 3] + 2) >> 2;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
levels.push(next);
|
|
44
|
+
}
|
|
45
|
+
return levels;
|
|
46
|
+
}
|
|
47
|
+
/** The power-of-two square an image of `w × h` is resampled to: its larger side rounded up, at most `maxSize`. */
|
|
48
|
+
export function squareSize(w, h, maxSize) {
|
|
49
|
+
let s = 1;
|
|
50
|
+
while (s < Math.max(w, h))
|
|
51
|
+
s <<= 1;
|
|
52
|
+
return Math.max(1, Math.min(s, maxSize));
|
|
53
|
+
}
|
|
54
|
+
/** An RGBA8 texture from level-0 pixels (mips built here). */
|
|
55
|
+
export function rgbaTexture(rgba, size, srgb) {
|
|
56
|
+
return { width: size, height: size, format: srgb ? 'rgba8unorm-srgb' : 'rgba8unorm', family: 'rgba8', srgb, block: 1, blockBytes: 4, levels: buildMips(rgba, size, srgb) };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decodes a PNG, JPEG or WebP into RGBA8 pixels resampled to a power-of-two square of at most
|
|
60
|
+
* `maxSize` (UVs are normalized, so a non-square image keeps its mapping; ADR-0066). Needs
|
|
61
|
+
* `createImageBitmap` and `OffscreenCanvas` (browsers and their workers).
|
|
62
|
+
*/
|
|
63
|
+
export async function decodeImagePixels(bytes, maxSize) {
|
|
64
|
+
if (typeof createImageBitmap !== 'function' || typeof OffscreenCanvas !== 'function') {
|
|
65
|
+
throw new YaliError('IMAGE_DECODE_UNAVAILABLE', 'This environment cannot decode images (no createImageBitmap / OffscreenCanvas).', 'Decode images in a browser or its workers; Node runs get no image textures.');
|
|
66
|
+
}
|
|
67
|
+
const bitmap = await createImageBitmap(new Blob([bytes]), { colorSpaceConversion: 'none', premultiplyAlpha: 'none' });
|
|
68
|
+
try {
|
|
69
|
+
const size = squareSize(bitmap.width, bitmap.height, maxSize);
|
|
70
|
+
const canvas = new OffscreenCanvas(size, size);
|
|
71
|
+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
72
|
+
if (!ctx)
|
|
73
|
+
throw new YaliError('IMAGE_DECODE', 'No 2D context for image resampling.', 'Retry; this should not happen in a browser.');
|
|
74
|
+
ctx.imageSmoothingEnabled = true;
|
|
75
|
+
ctx.imageSmoothingQuality = 'high';
|
|
76
|
+
ctx.drawImage(bitmap, 0, 0, size, size);
|
|
77
|
+
return { rgba: new Uint8Array(ctx.getImageData(0, 0, size, size).data.buffer), size };
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
bitmap.close();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** A texture from a PNG, JPEG or WebP: decoded, resampled square, mipmapped (`decodeImagePixels` + `buildMips`). */
|
|
84
|
+
export async function decodeImage(bytes, options) {
|
|
85
|
+
const { rgba, size } = await decodeImagePixels(bytes, options.maxSize);
|
|
86
|
+
return rgbaTexture(rgba, size, options.srgb);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* glTF keeps occlusion (R of its own texture) apart from metallic-roughness (G, B); the
|
|
90
|
+
* store's ORM slot wants both in one: R from `occlusion` (255 without), G and B from
|
|
91
|
+
* `metalRough` (255 without, so the factors apply unchanged). Inputs are square RGBA8 of `size`.
|
|
92
|
+
*/
|
|
93
|
+
export function mergeOrm(occlusion, metalRough, size) {
|
|
94
|
+
const out = new Uint8Array(size * size * 4);
|
|
95
|
+
for (let i = 0; i < size * size; i++) {
|
|
96
|
+
const o = i * 4;
|
|
97
|
+
out[o] = occlusion ? occlusion[o] : 255;
|
|
98
|
+
out[o + 1] = metalRough ? metalRough[o + 1] : 255;
|
|
99
|
+
out[o + 2] = metalRough ? metalRough[o + 2] : 255;
|
|
100
|
+
out[o + 3] = 255;
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" preserve="true" />
|
|
2
|
+
/**
|
|
3
|
+
* `@yali-engine/ktx2`: GPU-native KTX2 textures for Yali (M2 increment 3, ADR-0060). Read and write
|
|
4
|
+
* KTX2 containers of BC7, ASTC 4x4, ETC2 and RGBA8 data, and pick the variant a device can
|
|
5
|
+
* sample (`foo.bc7.ktx2`, `foo.astc.ktx2`, `foo.etc2.ktx2`, `foo.rgba8.ktx2`, as `yali convert`
|
|
6
|
+
* writes them). Basis-compressed KTX2 needs the optional `@yali-engine/basis` plugin.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* const k = parseKtx2(bytes);
|
|
10
|
+
* const info = formatOf(k); // { gpu: 'bc7-rgba-unorm-srgb', block: 4, bytes: 16, ... }
|
|
11
|
+
*/
|
|
12
|
+
export { type FormatInfo, levelBytes, pickVariant, type Variant, VARIANT_FEATURE, VARIANT_VK, VARIANTS, VK_FORMATS } from './formats.ts';
|
|
13
|
+
export { COLOR_MODEL, formatOf, type Ktx2, type Ktx2Input, parseKtx2, SUPERCOMPRESSION, writeKtx2 } from './ktx2.ts';
|
|
14
|
+
export { checkShape, type DecodedTexture, decodeKtx2, variantPath } from './decode.ts';
|
|
15
|
+
export { type Ktx2LoaderOptions, ktx2Loader, ktx2Plugin, type TextureAssets, variantFor } from './loader.ts';
|
|
16
|
+
export { buildMips, decodeImage, decodeImagePixels, mergeOrm, rgbaTexture, squareSize } from './image.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// The WebGPU types this package's API uses, for consumers (kept in the emitted .d.ts).
|
|
2
|
+
/// <reference types="@webgpu/types" preserve="true" />
|
|
3
|
+
/**
|
|
4
|
+
* `@yali-engine/ktx2`: GPU-native KTX2 textures for Yali (M2 increment 3, ADR-0060). Read and write
|
|
5
|
+
* KTX2 containers of BC7, ASTC 4x4, ETC2 and RGBA8 data, and pick the variant a device can
|
|
6
|
+
* sample (`foo.bc7.ktx2`, `foo.astc.ktx2`, `foo.etc2.ktx2`, `foo.rgba8.ktx2`, as `yali convert`
|
|
7
|
+
* writes them). Basis-compressed KTX2 needs the optional `@yali-engine/basis` plugin.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const k = parseKtx2(bytes);
|
|
11
|
+
* const info = formatOf(k); // { gpu: 'bc7-rgba-unorm-srgb', block: 4, bytes: 16, ... }
|
|
12
|
+
*/
|
|
13
|
+
export { levelBytes, pickVariant, VARIANT_FEATURE, VARIANT_VK, VARIANTS, VK_FORMATS } from "./formats.js";
|
|
14
|
+
export { COLOR_MODEL, formatOf, parseKtx2, SUPERCOMPRESSION, writeKtx2 } from "./ktx2.js";
|
|
15
|
+
export { checkShape, decodeKtx2, variantPath } from "./decode.js";
|
|
16
|
+
export { ktx2Loader, ktx2Plugin, variantFor } from "./loader.js";
|
|
17
|
+
export { buildMips, decodeImage, decodeImagePixels, mergeOrm, rgbaTexture, squareSize } from "./image.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,uDAAuD;AACvD;;;;;;;;;GASG;AACH,OAAO,EAAmB,UAAU,EAAE,WAAW,EAAgB,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACzI,OAAO,EAAE,WAAW,EAAE,QAAQ,EAA6B,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACrH,OAAO,EAAE,UAAU,EAAuB,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EAA0B,UAAU,EAAE,UAAU,EAAsB,UAAU,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/ktx2.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type FormatInfo } from './formats.ts';
|
|
2
|
+
/** KTX2 supercompression schemes (`supercompressionScheme`). */
|
|
3
|
+
export declare const SUPERCOMPRESSION: {
|
|
4
|
+
readonly none: 0;
|
|
5
|
+
readonly basisLZ: 1;
|
|
6
|
+
readonly zstd: 2;
|
|
7
|
+
readonly zlib: 3;
|
|
8
|
+
};
|
|
9
|
+
/** A parsed KTX2 file: its header fields and each mip level's bytes (views into the input). */
|
|
10
|
+
export interface Ktx2 {
|
|
11
|
+
readonly vkFormat: number;
|
|
12
|
+
readonly width: number;
|
|
13
|
+
readonly height: number;
|
|
14
|
+
/** 0 for a 2D texture. */
|
|
15
|
+
readonly depth: number;
|
|
16
|
+
/** 0 for a non-array texture. */
|
|
17
|
+
readonly layers: number;
|
|
18
|
+
readonly faces: number;
|
|
19
|
+
readonly supercompression: number;
|
|
20
|
+
/** The colour model of the data format descriptor (166 = UASTC, 163 = ETC1S). */
|
|
21
|
+
readonly colorModel: number;
|
|
22
|
+
/** True when the descriptor's transfer function is sRGB. */
|
|
23
|
+
readonly srgb: boolean;
|
|
24
|
+
/** Level 0 (full size) first. Each is the level's bytes as stored (supercompressed when the file is). */
|
|
25
|
+
readonly levels: readonly Uint8Array[];
|
|
26
|
+
/** The supercompression global data (BasisLZ codebooks), empty otherwise. */
|
|
27
|
+
readonly globalData: Uint8Array;
|
|
28
|
+
/** Key/value metadata (`KTXwriter`, `KTXorientation`…), values as UTF-8 without the trailing NUL. */
|
|
29
|
+
readonly keyValues: ReadonlyMap<string, string>;
|
|
30
|
+
}
|
|
31
|
+
/** Colour models a KTX2 file's descriptor can name that matter here (Khronos Data Format 1.3). */
|
|
32
|
+
export declare const COLOR_MODEL: {
|
|
33
|
+
readonly rgbsda: 1;
|
|
34
|
+
readonly bc7: 134;
|
|
35
|
+
readonly etc2: 161;
|
|
36
|
+
readonly astc: 162;
|
|
37
|
+
readonly etc1s: 163;
|
|
38
|
+
readonly uastc: 166;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Reads a KTX2 container (Khronos KTX 2.0). Validates the identifier and that every level
|
|
42
|
+
* lies inside the file; does not decode texel data.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const k = parseKtx2(new Uint8Array(await (await fetch('rock.bc7.ktx2')).arrayBuffer()));
|
|
46
|
+
* k.levels.length; // mip levels, full size first
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseKtx2(bytes: Uint8Array): Ktx2;
|
|
49
|
+
/** The format a parsed file holds, or a `KTX2_UNSUPPORTED` error naming what to do. */
|
|
50
|
+
export declare function formatOf(k: Ktx2): FormatInfo;
|
|
51
|
+
/** What `writeKtx2` writes: one 2D texture, its levels full size first, uncompressed in the file. */
|
|
52
|
+
export interface Ktx2Input {
|
|
53
|
+
readonly vkFormat: number;
|
|
54
|
+
readonly width: number;
|
|
55
|
+
readonly height: number;
|
|
56
|
+
readonly levels: readonly Uint8Array[];
|
|
57
|
+
/** Defaults to `{ KTXwriter: 'yali' }`. */
|
|
58
|
+
readonly keyValues?: Readonly<Record<string, string>>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Writes a GPU-native 2D KTX2 file (no supercompression; ADR-0060) with a basic data format
|
|
62
|
+
* descriptor for the format, levels stored smallest first as the format requires.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const file = writeKtx2({ vkFormat: 43, width: 2, height: 2, levels: [rgba, rgba1x1] });
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeKtx2(input: Ktx2Input): Uint8Array;
|
package/dist/ktx2.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
import { levelBytes, VK_FORMATS } from "./formats.js";
|
|
3
|
+
/** The 12-byte KTX2 identifier: «KTX 20»\r\n\x1A\n. */
|
|
4
|
+
const IDENTIFIER = [0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
5
|
+
const HEADER_BYTES = 80;
|
|
6
|
+
/** KTX2 supercompression schemes (`supercompressionScheme`). */
|
|
7
|
+
export const SUPERCOMPRESSION = { none: 0, basisLZ: 1, zstd: 2, zlib: 3 };
|
|
8
|
+
/** Colour models a KTX2 file's descriptor can name that matter here (Khronos Data Format 1.3). */
|
|
9
|
+
export const COLOR_MODEL = { rgbsda: 1, bc7: 134, etc2: 161, astc: 162, etc1s: 163, uastc: 166 };
|
|
10
|
+
/**
|
|
11
|
+
* Reads a KTX2 container (Khronos KTX 2.0). Validates the identifier and that every level
|
|
12
|
+
* lies inside the file; does not decode texel data.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const k = parseKtx2(new Uint8Array(await (await fetch('rock.bc7.ktx2')).arrayBuffer()));
|
|
16
|
+
* k.levels.length; // mip levels, full size first
|
|
17
|
+
*/
|
|
18
|
+
export function parseKtx2(bytes) {
|
|
19
|
+
if (bytes.byteLength < HEADER_BYTES || IDENTIFIER.some((b, i) => bytes[i] !== b)) {
|
|
20
|
+
throw new YaliError('KTX2_INVALID', 'The bytes are not a KTX2 file (the 12-byte identifier is missing).', 'Check the path serves a .ktx2 file (a 404 page or a KTX1 file looks like this).');
|
|
21
|
+
}
|
|
22
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
23
|
+
const u32 = (o) => dv.getUint32(o, true);
|
|
24
|
+
const u64 = (o) => {
|
|
25
|
+
const hi = dv.getUint32(o + 4, true);
|
|
26
|
+
if (hi !== 0)
|
|
27
|
+
throw new YaliError('KTX2_TOO_LARGE', 'A KTX2 offset or length is 4 GiB or more.', 'Split the texture: Yali loads files under 4 GiB.');
|
|
28
|
+
return dv.getUint32(o, true);
|
|
29
|
+
};
|
|
30
|
+
const [vkFormat, , width, height, depth, layers, faces, levelCount, supercompression] = [12, 16, 20, 24, 28, 32, 36, 40, 44].map(u32);
|
|
31
|
+
const [dfdOffset, dfdLength, kvdOffset, kvdLength] = [48, 52, 56, 60].map(u32);
|
|
32
|
+
const sgdOffset = u64(64);
|
|
33
|
+
const sgdLength = u64(72);
|
|
34
|
+
const inside = (o, n) => o + n <= bytes.byteLength;
|
|
35
|
+
const levels = [];
|
|
36
|
+
for (let l = 0; l < Math.max(1, levelCount); l++) {
|
|
37
|
+
const at = HEADER_BYTES + l * 24;
|
|
38
|
+
const offset = u64(at);
|
|
39
|
+
const length = u64(at + 8);
|
|
40
|
+
if (!inside(offset, length)) {
|
|
41
|
+
throw new YaliError('KTX2_TRUNCATED', `Level ${l} lies outside the file (offset ${offset}, ${length} bytes, file ${bytes.byteLength}).`, 'The download was cut short or the file is corrupt; re-export it.');
|
|
42
|
+
}
|
|
43
|
+
levels.push(bytes.subarray(offset, offset + length));
|
|
44
|
+
}
|
|
45
|
+
let colorModel = 0;
|
|
46
|
+
let srgb = false;
|
|
47
|
+
if (dfdLength >= 28 && inside(dfdOffset, dfdLength)) {
|
|
48
|
+
// dfdTotalSize, then the basic block: vendor/type, version/size, model/primaries/transfer/flags.
|
|
49
|
+
const w = u32(dfdOffset + 12);
|
|
50
|
+
colorModel = w & 0xff;
|
|
51
|
+
srgb = ((w >>> 16) & 0xff) === 2;
|
|
52
|
+
}
|
|
53
|
+
const keyValues = new Map();
|
|
54
|
+
if (inside(kvdOffset, kvdLength)) {
|
|
55
|
+
const dec = new TextDecoder();
|
|
56
|
+
for (let o = kvdOffset; o + 4 <= kvdOffset + kvdLength;) {
|
|
57
|
+
const n = u32(o);
|
|
58
|
+
const entry = bytes.subarray(o + 4, o + 4 + n);
|
|
59
|
+
const nul = entry.indexOf(0);
|
|
60
|
+
if (nul > 0) {
|
|
61
|
+
const end = entry[n - 1] === 0 ? n - 1 : n;
|
|
62
|
+
keyValues.set(dec.decode(entry.subarray(0, nul)), dec.decode(entry.subarray(nul + 1, end)));
|
|
63
|
+
}
|
|
64
|
+
o += 4 + n + ((4 - (n % 4)) % 4);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const globalData = sgdLength > 0 && inside(sgdOffset, sgdLength) ? bytes.subarray(sgdOffset, sgdOffset + sgdLength) : new Uint8Array(0);
|
|
68
|
+
return { vkFormat, width, height, depth, layers, faces, supercompression, colorModel, srgb, levels, globalData, keyValues };
|
|
69
|
+
}
|
|
70
|
+
/** The format a parsed file holds, or a `KTX2_UNSUPPORTED` error naming what to do. */
|
|
71
|
+
export function formatOf(k) {
|
|
72
|
+
const info = VK_FORMATS.get(k.vkFormat);
|
|
73
|
+
if (k.vkFormat === 0 || !info) {
|
|
74
|
+
const basis = k.colorModel === COLOR_MODEL.uastc || k.colorModel === COLOR_MODEL.etc1s || k.supercompression === SUPERCOMPRESSION.basisLZ;
|
|
75
|
+
throw new YaliError('KTX2_UNSUPPORTED', basis ? 'This KTX2 file is Basis-compressed (ETC1S / UASTC), which needs a transcoder.' : `This KTX2 file's vkFormat ${k.vkFormat} is not one Yali loads.`, basis
|
|
76
|
+
? 'Convert it to GPU-native variants with `yali convert`, or add the @yali-engine/basis plugin to transcode it in a worker (ADR-0060).'
|
|
77
|
+
: 'Use `yali convert` to write BC7 / ASTC 4x4 / ETC2 / RGBA8 variants.');
|
|
78
|
+
}
|
|
79
|
+
if (k.supercompression !== SUPERCOMPRESSION.none) {
|
|
80
|
+
throw new YaliError('KTX2_SUPERCOMPRESSED', `This ${info.gpu} KTX2 file is supercompressed (scheme ${k.supercompression}).`, 'Write GPU-native variants uncompressed and serve them with HTTP brotli (ADR-0060), or load Basis files through @yali-engine/basis.');
|
|
81
|
+
}
|
|
82
|
+
if (k.depth > 1 || k.faces !== 1 || k.layers > 1) {
|
|
83
|
+
throw new YaliError('KTX2_UNSUPPORTED', `This KTX2 file is ${k.depth > 1 ? '3D' : k.faces !== 1 ? 'a cube map' : 'an array'}; Yali loads 2D textures.`, 'Export each face or layer as its own 2D texture.');
|
|
84
|
+
}
|
|
85
|
+
k.levels.forEach((lv, l) => {
|
|
86
|
+
const want = levelBytes(info, Math.max(1, k.width >> l), Math.max(1, k.height >> l));
|
|
87
|
+
if (lv.byteLength !== want) {
|
|
88
|
+
throw new YaliError('KTX2_TRUNCATED', `Level ${l} holds ${lv.byteLength} bytes; ${info.gpu} at ${Math.max(1, k.width >> l)}×${Math.max(1, k.height >> l)} needs ${want}.`, 'The file is corrupt or mislabelled; re-export it.');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return info;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Writes a GPU-native 2D KTX2 file (no supercompression; ADR-0060) with a basic data format
|
|
95
|
+
* descriptor for the format, levels stored smallest first as the format requires.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* const file = writeKtx2({ vkFormat: 43, width: 2, height: 2, levels: [rgba, rgba1x1] });
|
|
99
|
+
*/
|
|
100
|
+
export function writeKtx2(input) {
|
|
101
|
+
const info = VK_FORMATS.get(input.vkFormat);
|
|
102
|
+
if (!info)
|
|
103
|
+
throw new YaliError('KTX2_UNSUPPORTED', `writeKtx2 does not know vkFormat ${input.vkFormat}.`, 'Use one of the formats in VK_FORMATS (BC7, ASTC 4x4, ETC2, RGBA8).');
|
|
104
|
+
input.levels.forEach((lv, l) => {
|
|
105
|
+
const want = levelBytes(info, Math.max(1, input.width >> l), Math.max(1, input.height >> l));
|
|
106
|
+
if (lv.byteLength !== want)
|
|
107
|
+
throw new YaliError('KTX2_BAD_LEVEL', `Level ${l} has ${lv.byteLength} bytes; it needs ${want}.`, 'Pass levels full size first, each half the previous size.');
|
|
108
|
+
});
|
|
109
|
+
const dfd = descriptor(input.vkFormat, info);
|
|
110
|
+
const enc = new TextEncoder();
|
|
111
|
+
const kv = Object.entries(input.keyValues ?? { KTXwriter: 'yali' }).sort(([a], [b]) => (a < b ? -1 : 1));
|
|
112
|
+
const kvParts = [];
|
|
113
|
+
for (const [k, v] of kv) {
|
|
114
|
+
const body = new Uint8Array([...enc.encode(k), 0, ...enc.encode(v), 0]);
|
|
115
|
+
const head = new Uint8Array(4);
|
|
116
|
+
new DataView(head.buffer).setUint32(0, body.byteLength, true);
|
|
117
|
+
kvParts.push(head, body, new Uint8Array((4 - (body.byteLength % 4)) % 4));
|
|
118
|
+
}
|
|
119
|
+
const kvdLength = kvParts.reduce((n, p) => n + p.byteLength, 0);
|
|
120
|
+
const levelCount = input.levels.length;
|
|
121
|
+
const dfdOffset = HEADER_BYTES + levelCount * 24;
|
|
122
|
+
const kvdOffset = dfdOffset + dfd.byteLength;
|
|
123
|
+
// Level data: smallest level first, each aligned to lcm(block bytes, 4).
|
|
124
|
+
const align = lcm(info.bytes, 4);
|
|
125
|
+
let at = kvdOffset + kvdLength;
|
|
126
|
+
const placed = new Array(levelCount);
|
|
127
|
+
for (let l = levelCount - 1; l >= 0; l--) {
|
|
128
|
+
at = Math.ceil(at / align) * align;
|
|
129
|
+
placed[l] = at;
|
|
130
|
+
at += input.levels[l].byteLength;
|
|
131
|
+
}
|
|
132
|
+
const out = new Uint8Array(at);
|
|
133
|
+
const dv = new DataView(out.buffer);
|
|
134
|
+
out.set(IDENTIFIER, 0);
|
|
135
|
+
// vkFormat, typeSize (1: byte components or blocks), size, depth 0, layers 0, faces 1, levels, no supercompression.
|
|
136
|
+
[input.vkFormat, 1, input.width, input.height, 0, 0, 1, levelCount, 0].forEach((v, i) => dv.setUint32(12 + i * 4, v, true));
|
|
137
|
+
dv.setUint32(48, dfdOffset, true);
|
|
138
|
+
dv.setUint32(52, dfd.byteLength, true);
|
|
139
|
+
dv.setUint32(56, kvdLength > 0 ? kvdOffset : 0, true);
|
|
140
|
+
dv.setUint32(60, kvdLength, true);
|
|
141
|
+
for (let l = 0; l < levelCount; l++) {
|
|
142
|
+
const o = HEADER_BYTES + l * 24;
|
|
143
|
+
dv.setUint32(o, placed[l], true);
|
|
144
|
+
dv.setUint32(o + 8, input.levels[l].byteLength, true);
|
|
145
|
+
dv.setUint32(o + 16, input.levels[l].byteLength, true);
|
|
146
|
+
out.set(input.levels[l], placed[l]);
|
|
147
|
+
}
|
|
148
|
+
out.set(dfd, dfdOffset);
|
|
149
|
+
let k = kvdOffset;
|
|
150
|
+
for (const p of kvParts) {
|
|
151
|
+
out.set(p, k);
|
|
152
|
+
k += p.byteLength;
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
/** The basic data format descriptor (Khronos Data Format 1.3) for one of `VK_FORMATS`. */
|
|
157
|
+
function descriptor(vkFormat, info) {
|
|
158
|
+
// Samples: [bitOffset, bitLength - 1, channel | qualifiers << 4, lower, upper].
|
|
159
|
+
const full = 0xffffffff;
|
|
160
|
+
const linear = 0x10;
|
|
161
|
+
let model;
|
|
162
|
+
let samples;
|
|
163
|
+
switch (info.family) {
|
|
164
|
+
case 'rgba8':
|
|
165
|
+
model = COLOR_MODEL.rgbsda;
|
|
166
|
+
samples = [[0, 7, 0, 0, 255], [8, 7, 1, 0, 255], [16, 7, 2, 0, 255], [24, 7, 15 | (info.srgb ? linear : 0), 0, 255]];
|
|
167
|
+
break;
|
|
168
|
+
case 'bc7':
|
|
169
|
+
model = COLOR_MODEL.bc7;
|
|
170
|
+
samples = [[0, 127, 0, 0, full]];
|
|
171
|
+
break;
|
|
172
|
+
case 'astc':
|
|
173
|
+
model = COLOR_MODEL.astc;
|
|
174
|
+
samples = [[0, 127, 0, 0, full]];
|
|
175
|
+
break;
|
|
176
|
+
case 'etc2':
|
|
177
|
+
model = COLOR_MODEL.etc2;
|
|
178
|
+
// RGBA: the EAC alpha half first (channel 15), then the colour (channel 2).
|
|
179
|
+
samples = vkFormat === 151 || vkFormat === 152 ? [[0, 63, 15, 0, full], [64, 63, 2, 0, full]] : [[0, 63, 2, 0, full]];
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
const blockSize = 24 + samples.length * 16;
|
|
183
|
+
const out = new Uint8Array(4 + blockSize);
|
|
184
|
+
const dv = new DataView(out.buffer);
|
|
185
|
+
dv.setUint32(0, out.byteLength, true);
|
|
186
|
+
dv.setUint32(4, 0, true); // vendor Khronos, descriptor type basic
|
|
187
|
+
dv.setUint32(8, 2 | (blockSize << 16), true); // version 1.3 (2)
|
|
188
|
+
// BT.709 primaries; transfer 2 = sRGB, 1 = linear; straight alpha.
|
|
189
|
+
dv.setUint32(12, model | (1 << 8) | ((info.srgb ? 2 : 1) << 16), true);
|
|
190
|
+
const d = info.block - 1;
|
|
191
|
+
out.set([d, d, 0, 0], 16);
|
|
192
|
+
out[20] = info.bytes; // bytesPlane0
|
|
193
|
+
samples.forEach(([offset, length, channel, lower, upper], i) => {
|
|
194
|
+
const o = 28 + i * 16;
|
|
195
|
+
dv.setUint16(o, offset, true);
|
|
196
|
+
out[o + 2] = length;
|
|
197
|
+
out[o + 3] = channel;
|
|
198
|
+
dv.setUint32(o + 8, lower, true);
|
|
199
|
+
dv.setUint32(o + 12, upper, true);
|
|
200
|
+
});
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
function lcm(a, b) {
|
|
204
|
+
const gcd = (x, y) => (y === 0 ? x : gcd(y, x % y));
|
|
205
|
+
return (a * b) / gcd(a, b);
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=ktx2.js.map
|
package/dist/ktx2.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktx2.js","sourceRoot":"","sources":["../src/ktx2.ts"],"names":[],"mappings":"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"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AssetLoader, DecodePool, YaliPlugin } from '@yali-engine/core';
|
|
2
|
+
import type { TextureRef, TextureStore } from '@yali-engine/render';
|
|
3
|
+
import { type DecodedTexture } from './decode.ts';
|
|
4
|
+
import { type Variant } from './formats.ts';
|
|
5
|
+
/** `ktx2Loader` options. */
|
|
6
|
+
export interface Ktx2LoaderOptions {
|
|
7
|
+
/** The render plugin's texture store (read when a texture uploads, so recovery's store is used). */
|
|
8
|
+
readonly textures: () => TextureStore;
|
|
9
|
+
/** Decode on these workers (they must serve `ktx2`: `decodeKtx2`, or `@yali-engine/basis`'s decoder). Without one, decode inline. */
|
|
10
|
+
readonly pool?: DecodePool;
|
|
11
|
+
/** The decoder name on the pool (default `ktx2`; `@yali-engine/basis` registers `ktx2-basis`). */
|
|
12
|
+
readonly decoder?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The `.ktx2` asset loader (ADR-0060): decodes on the decode pool (else inline), then takes a
|
|
16
|
+
* layer in the render plugin's texture store, where the levels stream in smallest first.
|
|
17
|
+
* Its value is the `TextureRef` a material's map points at, or null (the fallback) before
|
|
18
|
+
* it is ready or after it failed (a material then draws white, or the magenta checker for a
|
|
19
|
+
* handle that failed). Load the variant for the device: `textures.texturePath('rock.ktx2')`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* assets.register(ktx2Loader({ textures: () => render.textures, pool }));
|
|
23
|
+
* const tex = assets.load<TextureRef | null>(variantPath('rock.ktx2', 'bc7'));
|
|
24
|
+
* render.materials.add({ baseColor: [1, 1, 1, 1], baseColorMap: tex });
|
|
25
|
+
*/
|
|
26
|
+
export declare function ktx2Loader(options: Ktx2LoaderOptions): AssetLoader<TextureRef | null, DecodedTexture>;
|
|
27
|
+
/** What `ktx2Plugin` provides (as the `textures` resource). */
|
|
28
|
+
export interface TextureAssets {
|
|
29
|
+
/** The variant this device loads (`bc7`, `astc`, `etc2` or `rgba8`): the texture store's family. */
|
|
30
|
+
readonly variant: Variant;
|
|
31
|
+
/** The device's file for a logical texture path: `rock.ktx2` → `rock.bc7.ktx2`. */
|
|
32
|
+
path(logical: string): string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Registers `ktx2Loader` with the asset server and provides `textures`, which names the
|
|
36
|
+
* variant this device loads (the texture store's family, chosen from its features).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const app = App.create({ core, plugins: [renderPlugin({ canvas }), assetsPlugin(), ktx2Plugin({ pool }), game] });
|
|
40
|
+
* // in game: assets.load(app.resource<TextureAssets>('textures').path('rock.ktx2'))
|
|
41
|
+
*/
|
|
42
|
+
export declare function ktx2Plugin(options?: {
|
|
43
|
+
pool?: DecodePool;
|
|
44
|
+
decoder?: string;
|
|
45
|
+
}): YaliPlugin;
|
|
46
|
+
/** The variant a device with `features` loads (the same rule as the render plugin's `pickFamily`). */
|
|
47
|
+
export declare const variantFor: (features: ReadonlySet<string>) => Variant;
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { decodeKtx2, variantPath } from "./decode.js";
|
|
2
|
+
import { pickVariant } from "./formats.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `.ktx2` asset loader (ADR-0060): decodes on the decode pool (else inline), then takes a
|
|
5
|
+
* layer in the render plugin's texture store, where the levels stream in smallest first.
|
|
6
|
+
* Its value is the `TextureRef` a material's map points at, or null (the fallback) before
|
|
7
|
+
* it is ready or after it failed (a material then draws white, or the magenta checker for a
|
|
8
|
+
* handle that failed). Load the variant for the device: `textures.texturePath('rock.ktx2')`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* assets.register(ktx2Loader({ textures: () => render.textures, pool }));
|
|
12
|
+
* const tex = assets.load<TextureRef | null>(variantPath('rock.ktx2', 'bc7'));
|
|
13
|
+
* render.materials.add({ baseColor: [1, 1, 1, 1], baseColorMap: tex });
|
|
14
|
+
*/
|
|
15
|
+
export function ktx2Loader(options) {
|
|
16
|
+
const store = new Map();
|
|
17
|
+
return {
|
|
18
|
+
type: 'texture',
|
|
19
|
+
extensions: ['.ktx2'],
|
|
20
|
+
// The store's family goes along, for a decoder that transcodes (`@yali-engine/basis`).
|
|
21
|
+
decode: (bytes, ctx) => options.pool ? options.pool.decode(options.decoder ?? 'ktx2', bytes, { signal: ctx.signal, args: { family: options.textures().family } }) : decodeKtx2(bytes),
|
|
22
|
+
upload: (d) => {
|
|
23
|
+
const textures = options.textures();
|
|
24
|
+
const ref = textures.add(d);
|
|
25
|
+
store.set(ref, textures);
|
|
26
|
+
return ref;
|
|
27
|
+
},
|
|
28
|
+
byteSize: (ref) => ref?.bytes ?? 0,
|
|
29
|
+
fallback: () => null,
|
|
30
|
+
dispose: (ref) => {
|
|
31
|
+
if (!ref)
|
|
32
|
+
return;
|
|
33
|
+
store.get(ref)?.remove(ref);
|
|
34
|
+
store.delete(ref);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Registers `ktx2Loader` with the asset server and provides `textures`, which names the
|
|
40
|
+
* variant this device loads (the texture store's family, chosen from its features).
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const app = App.create({ core, plugins: [renderPlugin({ canvas }), assetsPlugin(), ktx2Plugin({ pool }), game] });
|
|
44
|
+
* // in game: assets.load(app.resource<TextureAssets>('textures').path('rock.ktx2'))
|
|
45
|
+
*/
|
|
46
|
+
export function ktx2Plugin(options = {}) {
|
|
47
|
+
return {
|
|
48
|
+
name: '@yali-engine/ktx2',
|
|
49
|
+
version: '0.4.0',
|
|
50
|
+
dependencies: [
|
|
51
|
+
{ name: '@yali-engine/render', range: '^0.4.0' },
|
|
52
|
+
{ name: '@yali-engine/assets', range: '^0.4.0' },
|
|
53
|
+
],
|
|
54
|
+
build(b) {
|
|
55
|
+
const render = b.app.resource('render');
|
|
56
|
+
const assets = b.app.resource('assets');
|
|
57
|
+
assets.register(ktx2Loader({ textures: () => render.textures, pool: options.pool, decoder: options.decoder }));
|
|
58
|
+
const variant = render.textures.family;
|
|
59
|
+
b.provide('textures', { variant, path: (logical) => variantPath(logical, variant) });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** The variant a device with `features` loads (the same rule as the render plugin's `pickFamily`). */
|
|
64
|
+
export const variantFor = (features) => pickVariant(features);
|
|
65
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAgB,MAAM,cAAc,CAAC;AAYzD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAClD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,UAAU,EAAE,CAAC,OAAO,CAAC;QACrB,uFAAuF;QACvF,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAiB,OAAO,CAAC,OAAO,IAAI,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;QAC/K,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YACzB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC;QAClC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;QACpB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACf,IAAI,CAAC,GAAG;gBAAE,OAAO;YACjB,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,UAAmD,EAAE;IAC9E,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,OAAO;QAChB,YAAY,EAAE;YACZ,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE;YAChD,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE;SACjD;QACD,KAAK,CAAC,CAAC;YACL,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAgB,QAAQ,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAuD,QAAQ,CAAC,CAAC;YAC9F,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAkC,CAAC,CAAC;YAChJ,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAwB,CAAC;YACzD,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAA0B,CAAC,CAAC;QACvH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,sGAAsG;AACtG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAA6B,EAAW,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yali-engine/ktx2",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Yali KTX2 textures: read and write GPU-native KTX2 (BC7, ASTC 4x4, ETC2, RGBA8), pick the device's variant, decode in a worker (ADR-0060).",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@webgpu/types": "^0.1.60",
|
|
22
|
+
"@yali-engine/core": "0.4.0",
|
|
23
|
+
"@yali-engine/render": "0.4.0"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
27
|
+
"test": "node --experimental-strip-types --no-warnings --test \"test/*.test.ts\""
|
|
28
|
+
}
|
|
6
29
|
}
|
package/README.md
DELETED