gputex 0.0.3 → 0.0.5
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/README.md +14 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +34 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ pnpm add gputex
|
|
|
14
14
|
bun add gputex
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`three` is a peer dependency (`>=0.
|
|
17
|
+
`three` is a peer dependency (`>=0.180`).
|
|
18
18
|
|
|
19
19
|
## Formats
|
|
20
20
|
|
|
@@ -43,12 +43,12 @@ const { texture, format } = await compressTexture('/cobblestone.avif', {
|
|
|
43
43
|
material.map = texture
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
### `
|
|
46
|
+
### `GputexLoader` — Three.js Loader
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import {
|
|
49
|
+
import { GputexLoader } from 'gputex'
|
|
50
50
|
|
|
51
|
-
const loader = new
|
|
51
|
+
const loader = new GputexLoader()
|
|
52
52
|
loader.hint = 'normal'
|
|
53
53
|
loader.mipmaps = true
|
|
54
54
|
const normalMap = await loader.loadAsync('/brick_normal.png')
|
|
@@ -57,14 +57,14 @@ material.normalMap = normalMap
|
|
|
57
57
|
|
|
58
58
|
### React Three Fiber
|
|
59
59
|
|
|
60
|
-
The `
|
|
60
|
+
The `GputexLoader` works with R3F's `useLoader`:
|
|
61
61
|
|
|
62
62
|
```tsx
|
|
63
63
|
import { useLoader } from '@react-three/fiber'
|
|
64
|
-
import {
|
|
64
|
+
import { GputexLoader } from 'gputex'
|
|
65
65
|
|
|
66
66
|
function Scene() {
|
|
67
|
-
const texture = useLoader(
|
|
67
|
+
const texture = useLoader(GputexLoader, '/cobblestone.avif', loader => {
|
|
68
68
|
loader.hint = 'color'
|
|
69
69
|
loader.colorSpace = 'srgb'
|
|
70
70
|
loader.mipmaps = true
|
|
@@ -84,11 +84,11 @@ For a reusable hook with metadata access:
|
|
|
84
84
|
```tsx
|
|
85
85
|
import { useLayoutEffect } from 'react'
|
|
86
86
|
import { useLoader } from '@react-three/fiber'
|
|
87
|
-
import {
|
|
87
|
+
import { GputexLoader } from 'gputex'
|
|
88
88
|
import type { TextureHint } from 'gputex'
|
|
89
89
|
|
|
90
90
|
function useGputex(url: string, options?: { hint?: TextureHint; colorSpace?: 'srgb' | 'linear'; mipmaps?: boolean }) {
|
|
91
|
-
const texture = useLoader(
|
|
91
|
+
const texture = useLoader(GputexLoader, url, loader => {
|
|
92
92
|
if (options?.hint !== undefined) loader.hint = options.hint
|
|
93
93
|
if (options?.colorSpace !== undefined) loader.colorSpace = options.colorSpace
|
|
94
94
|
if (options?.mipmaps !== undefined) loader.mipmaps = options.mipmaps
|
|
@@ -102,7 +102,7 @@ useGputex.preload = (
|
|
|
102
102
|
url: string,
|
|
103
103
|
options?: { hint?: TextureHint; colorSpace?: 'srgb' | 'linear'; mipmaps?: boolean },
|
|
104
104
|
) => {
|
|
105
|
-
useLoader.preload(
|
|
105
|
+
useLoader.preload(GputexLoader, url, loader => {
|
|
106
106
|
if (options?.hint !== undefined) loader.hint = options.hint
|
|
107
107
|
if (options?.colorSpace !== undefined) loader.colorSpace = options.colorSpace
|
|
108
108
|
if (options?.mipmaps !== undefined) loader.mipmaps = options.mipmaps
|
|
@@ -158,6 +158,10 @@ encoder.destroy()
|
|
|
158
158
|
- `texture-compression-bc` (desktop) or `texture-compression-astc` (mobile) for compressed output
|
|
159
159
|
- Falls back to uncompressed RGBA8 when neither is available
|
|
160
160
|
|
|
161
|
+
## Device-specific workarounds
|
|
162
|
+
|
|
163
|
+
- Black texture on Google Pixel 10: `copyExternalImageToTexture` produces black textures on the Pixel 10's PowerVR DXT GPU (vendor `img-tec`, architecture `d-series`). Worked around by uploading via `writeTexture` with rasterised pixel data instead.
|
|
164
|
+
|
|
161
165
|
## Acknowledgements
|
|
162
166
|
|
|
163
167
|
The concept of encoding images on the GPU on the fly via compute shaders was first introduced by [spark.js](https://ludicon.com/sparkjs/), which is a much more robust solution for users who can afford its license. GPUtex is not derived from Spark and its encoders have been implemented from scratch using official references, which have been ported to TypeScript, and then converted to WGSL via AI. For any serious production use of GPU-compressed textures, Spark is the recommended choice over GPUtex.
|
package/dist/index.d.ts
CHANGED
|
@@ -274,7 +274,7 @@ interface CompressResult {
|
|
|
274
274
|
}
|
|
275
275
|
declare function compressTexture(source: CompressTextureSource, options?: CompressOptions): Promise<CompressResult>;
|
|
276
276
|
|
|
277
|
-
declare class
|
|
277
|
+
declare class GputexLoader extends Loader<Texture> {
|
|
278
278
|
/** Format-selection hint. Default 'color'. */
|
|
279
279
|
hint: TextureHint;
|
|
280
280
|
/** Pick the sRGB or linear variant of the chosen format. Default 'srgb'. */
|
|
@@ -329,4 +329,4 @@ declare function generateMipChain(level0: MipLevel): MipLevel[];
|
|
|
329
329
|
*/
|
|
330
330
|
declare function padToBlockMultiple(level: MipLevel): MipLevel;
|
|
331
331
|
|
|
332
|
-
export { ASTC4x4Encoder, BC1Encoder, BC5Encoder, BC7Encoder, type Capabilities, type CompressOptions, type CompressResult, type CompressTextureSource, type EncodeBytesResult, type EncodeCallOptions, type EncodeResult, Encoder, type EncoderConstructor, type EncoderImageSource, type EncoderOptions, type FeatureProvider, type FormatSelection, type FormatVariant, type MipLevel, type SelectFormatOptions, TextureFormat, type TextureHint,
|
|
332
|
+
export { ASTC4x4Encoder, BC1Encoder, BC5Encoder, BC7Encoder, type Capabilities, type CompressOptions, type CompressResult, type CompressTextureSource, type EncodeBytesResult, type EncodeCallOptions, type EncodeResult, Encoder, type EncoderConstructor, type EncoderImageSource, type EncoderOptions, type FeatureProvider, type FormatSelection, type FormatVariant, GputexLoader, type MipLevel, type SelectFormatOptions, TextureFormat, type TextureHint, WebGPUFeature, compressTexture, detectCapabilities, generateMipChain, padToBlockMultiple, selectFormat };
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,25 @@ import {
|
|
|
50
50
|
SRGBColorSpace,
|
|
51
51
|
RepeatWrapping
|
|
52
52
|
} from "three";
|
|
53
|
+
|
|
54
|
+
// src/workarounds.ts
|
|
55
|
+
function needsWriteTextureWorkaround(adapter) {
|
|
56
|
+
const { vendor, architecture } = adapter.info ?? {};
|
|
57
|
+
return vendor === "img-tec" && architecture === "d-series";
|
|
58
|
+
}
|
|
59
|
+
function uploadSourceTexture(device, srcTex, source, width, height, flipY, useWriteTexture) {
|
|
60
|
+
if (useWriteTexture && source instanceof ImageData) {
|
|
61
|
+
device.queue.writeTexture({ texture: srcTex }, source.data, { bytesPerRow: width * 4 }, [width, height, 1]);
|
|
62
|
+
} else {
|
|
63
|
+
device.queue.copyExternalImageToTexture({ source, flipY }, { texture: srcTex }, [
|
|
64
|
+
width,
|
|
65
|
+
height,
|
|
66
|
+
1
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/Encoder.ts
|
|
53
72
|
var Encoder = class {
|
|
54
73
|
/**
|
|
55
74
|
* Subclasses set this to the WebGPU feature string the output texture
|
|
@@ -184,9 +203,11 @@ var Encoder = class {
|
|
|
184
203
|
label: `${this.label}-src`,
|
|
185
204
|
size: [paddedWidth, paddedHeight, 1],
|
|
186
205
|
format: "rgba8unorm",
|
|
206
|
+
// RENDER_ATTACHMENT is required by copyExternalImageToTexture
|
|
207
|
+
// (internally a blit) even though we never render into this texture.
|
|
187
208
|
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
|
|
188
209
|
});
|
|
189
|
-
device
|
|
210
|
+
uploadSourceTexture(device, srcTex, source, width, height, flipY, source instanceof ImageData);
|
|
190
211
|
const dstBuffer = device.createBuffer({
|
|
191
212
|
label: `${this.label}-dst`,
|
|
192
213
|
size: outByteLen,
|
|
@@ -608,8 +629,16 @@ async function compressTexture(source, options = {}) {
|
|
|
608
629
|
encoder = await selection.encoderClass.create();
|
|
609
630
|
}
|
|
610
631
|
try {
|
|
632
|
+
const needsWriteTexture = needsWriteTextureWorkaround(adapter);
|
|
611
633
|
if (!mipmaps) {
|
|
612
|
-
|
|
634
|
+
let bytes;
|
|
635
|
+
if (needsWriteTexture) {
|
|
636
|
+
const level02 = bitmapToMipLevel(bitmap, flipY);
|
|
637
|
+
const imageData = mipLevelToImageData(level02);
|
|
638
|
+
bytes = await encoder.encodeToBytes(imageData);
|
|
639
|
+
} else {
|
|
640
|
+
bytes = await encoder.encodeToBytes(bitmap, { flipY });
|
|
641
|
+
}
|
|
613
642
|
const tex2 = encoder.buildMippedTexture([bytes], { colorSpace });
|
|
614
643
|
return {
|
|
615
644
|
texture: tex2,
|
|
@@ -658,9 +687,9 @@ async function compressTexture(source, options = {}) {
|
|
|
658
687
|
}
|
|
659
688
|
}
|
|
660
689
|
|
|
661
|
-
// src/
|
|
690
|
+
// src/GputexLoader.ts
|
|
662
691
|
import { Loader } from "three";
|
|
663
|
-
var
|
|
692
|
+
var GputexLoader = class extends Loader {
|
|
664
693
|
/** Format-selection hint. Default 'color'. */
|
|
665
694
|
hint = "color";
|
|
666
695
|
/** Pick the sRGB or linear variant of the chosen format. Default 'srgb'. */
|
|
@@ -728,8 +757,8 @@ export {
|
|
|
728
757
|
BC5Encoder,
|
|
729
758
|
BC7Encoder,
|
|
730
759
|
Encoder,
|
|
760
|
+
GputexLoader,
|
|
731
761
|
TextureFormat,
|
|
732
|
-
WebGPUCompressedTextureLoader,
|
|
733
762
|
WebGPUFeature,
|
|
734
763
|
compressTexture,
|
|
735
764
|
detectCapabilities,
|