@luma.gl/engine 9.3.0-alpha.4 → 9.3.0-alpha.6
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/animation-loop/animation-loop.d.ts +8 -4
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +70 -43
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +7 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -1
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +23 -6
- package/dist/animation-loop/request-animation-frame.js.map +1 -1
- package/dist/dist.dev.js +679 -927
- package/dist/dist.min.js +39 -240
- package/dist/dynamic-texture/dynamic-texture.d.ts +3 -3
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -1
- package/dist/dynamic-texture/dynamic-texture.js +170 -52
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -1
- package/dist/dynamic-texture/texture-data.d.ts +4 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -1
- package/dist/dynamic-texture/texture-data.js +8 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -1
- package/dist/factories/pipeline-factory.d.ts +7 -5
- package/dist/factories/pipeline-factory.d.ts.map +1 -1
- package/dist/factories/pipeline-factory.js +71 -36
- package/dist/factories/pipeline-factory.js.map +1 -1
- package/dist/factories/shader-factory.d.ts +0 -3
- package/dist/factories/shader-factory.d.ts.map +1 -1
- package/dist/factories/shader-factory.js +13 -19
- package/dist/factories/shader-factory.js.map +1 -1
- package/dist/geometries/cone-geometry.d.ts +3 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +2 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/index.cjs +683 -922
- package/dist/index.cjs.map +4 -4
- package/dist/model/model.d.ts +3 -1
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +11 -9
- package/dist/model/model.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +10 -8
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/clip-space.js +7 -7
- package/dist/modules/picking/index-picking.d.ts +1 -1
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +0 -6
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/passes/get-fragment-shader.js +11 -30
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +0 -2
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +4 -31
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/scenegraph/group-node.d.ts +5 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +12 -0
- package/dist/scenegraph/group-node.js.map +1 -1
- package/dist/scenegraph/model-node.d.ts +2 -2
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js.map +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +23 -15
- package/dist/scenegraph/scenegraph-node.js.map +1 -1
- package/package.json +2 -2
- package/src/animation-loop/animation-loop.ts +75 -46
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/dynamic-texture/dynamic-texture.ts +226 -65
- package/src/dynamic-texture/texture-data.ts +14 -0
- package/src/factories/pipeline-factory.ts +87 -46
- package/src/factories/shader-factory.ts +16 -20
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/model/model.ts +14 -10
- package/src/models/billboard-texture-model.ts +10 -8
- package/src/models/clip-space.ts +7 -7
- package/src/modules/picking/index-picking.ts +0 -6
- package/src/passes/get-fragment-shader.ts +11 -30
- package/src/passes/shader-pass-renderer.ts +4 -33
- package/src/scenegraph/group-node.ts +16 -0
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +27 -16
- package/dist/dynamic-texture/mipmaps.d.ts +0 -6
- package/dist/dynamic-texture/mipmaps.d.ts.map +0 -1
- package/dist/dynamic-texture/mipmaps.js +0 -441
- package/dist/dynamic-texture/mipmaps.js.map +0 -1
- package/src/dynamic-texture/mipmaps.ts +0 -517
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// luma.gl, MIT license
|
|
2
2
|
// Copyright (c) vis.gl contributors
|
|
3
3
|
|
|
4
|
-
import type {TextureProps, SamplerProps, TextureView, Device} from '@luma.gl/core';
|
|
4
|
+
import type {TextureProps, SamplerProps, TextureView, Device, TextureFormat} from '@luma.gl/core';
|
|
5
5
|
|
|
6
6
|
import {Texture, Sampler, log} from '@luma.gl/core';
|
|
7
7
|
|
|
@@ -26,10 +26,10 @@ import {
|
|
|
26
26
|
type TextureArrayData,
|
|
27
27
|
type TextureCubeArrayData,
|
|
28
28
|
type TextureCubeData,
|
|
29
|
-
type TextureImageData,
|
|
30
29
|
|
|
31
30
|
// Helpers
|
|
32
31
|
getTextureSizeFromData,
|
|
32
|
+
resolveTextureImageFormat,
|
|
33
33
|
getTexture1DSubresources,
|
|
34
34
|
getTexture2DSubresources,
|
|
35
35
|
getTexture3DSubresources,
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
getTextureArraySubresources,
|
|
38
38
|
getTextureCubeArraySubresources
|
|
39
39
|
} from './texture-data';
|
|
40
|
-
import {generateMipmap} from './mipmaps';
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
42
|
* Properties for a dynamic texture
|
|
@@ -131,7 +130,7 @@ export class DynamicTexture {
|
|
|
131
130
|
}
|
|
132
131
|
|
|
133
132
|
/** @note Fire and forget; caller can await `ready` */
|
|
134
|
-
async initAsync(originalPropsWithAsyncData:
|
|
133
|
+
async initAsync(originalPropsWithAsyncData: DynamicTextureProps): Promise<void> {
|
|
135
134
|
try {
|
|
136
135
|
// TODO - Accept URL string for 2D: turn into ExternalImage promise
|
|
137
136
|
// const dataProps =
|
|
@@ -141,6 +140,11 @@ export class DynamicTexture {
|
|
|
141
140
|
|
|
142
141
|
const propsWithSyncData = await this._loadAllData(originalPropsWithAsyncData);
|
|
143
142
|
this._checkNotDestroyed();
|
|
143
|
+
const subresources = propsWithSyncData.data ? getTextureSubresources(propsWithSyncData) : [];
|
|
144
|
+
const userProvidedFormat =
|
|
145
|
+
'format' in originalPropsWithAsyncData && originalPropsWithAsyncData.format !== undefined;
|
|
146
|
+
const userProvidedUsage =
|
|
147
|
+
'usage' in originalPropsWithAsyncData && originalPropsWithAsyncData.usage !== undefined;
|
|
144
148
|
|
|
145
149
|
// Deduce size when not explicitly provided
|
|
146
150
|
// TODO - what about depth?
|
|
@@ -162,15 +166,32 @@ export class DynamicTexture {
|
|
|
162
166
|
throw new Error(`${this} size could not be determined or was zero`);
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
// Normalize caller-provided subresources into one validated mip chain description.
|
|
170
|
+
const textureData = analyzeTextureSubresources(this.device, subresources, size, {
|
|
171
|
+
format: userProvidedFormat ? originalPropsWithAsyncData.format : undefined
|
|
172
|
+
});
|
|
173
|
+
const resolvedFormat = textureData.format ?? this.props.format;
|
|
174
|
+
|
|
165
175
|
// Create a minimal TextureProps and validate via `satisfies`
|
|
166
176
|
const baseTextureProps = {
|
|
167
177
|
...this.props,
|
|
168
178
|
...size,
|
|
179
|
+
format: resolvedFormat,
|
|
169
180
|
mipLevels: 1, // temporary; updated below
|
|
170
181
|
data: undefined
|
|
171
182
|
} satisfies TextureProps;
|
|
172
183
|
|
|
173
|
-
if (this.device.
|
|
184
|
+
if (this.device.isTextureFormatCompressed(resolvedFormat) && !userProvidedUsage) {
|
|
185
|
+
baseTextureProps.usage = Texture.SAMPLE | Texture.COPY_DST;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Explicit mip arrays take ownership of the mip chain; otherwise we may auto-generate it.
|
|
189
|
+
const shouldGenerateMipmaps =
|
|
190
|
+
this.props.mipmaps &&
|
|
191
|
+
!textureData.hasExplicitMipChain &&
|
|
192
|
+
!this.device.isTextureFormatCompressed(resolvedFormat);
|
|
193
|
+
|
|
194
|
+
if (this.device.type === 'webgpu' && shouldGenerateMipmaps) {
|
|
174
195
|
const requiredUsage =
|
|
175
196
|
this.props.dimension === '3d'
|
|
176
197
|
? Texture.SAMPLE | Texture.STORAGE | Texture.COPY_DST | Texture.COPY_SRC
|
|
@@ -180,8 +201,9 @@ export class DynamicTexture {
|
|
|
180
201
|
|
|
181
202
|
// Compute mip levels (auto clamps to max)
|
|
182
203
|
const maxMips = this.device.getMipLevelCount(baseTextureProps.width, baseTextureProps.height);
|
|
183
|
-
const desired =
|
|
184
|
-
|
|
204
|
+
const desired = textureData.hasExplicitMipChain
|
|
205
|
+
? textureData.mipLevels
|
|
206
|
+
: this.props.mipLevels === 'auto'
|
|
185
207
|
? maxMips
|
|
186
208
|
: Math.max(1, Math.min(maxMips, this.props.mipLevels ?? 1));
|
|
187
209
|
|
|
@@ -192,33 +214,15 @@ export class DynamicTexture {
|
|
|
192
214
|
this._view = this.texture.view;
|
|
193
215
|
|
|
194
216
|
// Upload data if provided
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
case '1d':
|
|
198
|
-
this.setTexture1DData(propsWithSyncData.data);
|
|
199
|
-
break;
|
|
200
|
-
case '2d':
|
|
201
|
-
this.setTexture2DData(propsWithSyncData.data);
|
|
202
|
-
break;
|
|
203
|
-
case '3d':
|
|
204
|
-
this.setTexture3DData(propsWithSyncData.data);
|
|
205
|
-
break;
|
|
206
|
-
case '2d-array':
|
|
207
|
-
this.setTextureArrayData(propsWithSyncData.data);
|
|
208
|
-
break;
|
|
209
|
-
case 'cube':
|
|
210
|
-
this.setTextureCubeData(propsWithSyncData.data);
|
|
211
|
-
break;
|
|
212
|
-
case 'cube-array':
|
|
213
|
-
this.setTextureCubeArrayData(propsWithSyncData.data);
|
|
214
|
-
break;
|
|
215
|
-
default: {
|
|
216
|
-
throw new Error(`Unhandled dimension ${propsWithSyncData.dimension}`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
217
|
+
if (textureData.subresources.length) {
|
|
218
|
+
this._setTextureSubresources(textureData.subresources);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
if (this.props.mipmaps) {
|
|
221
|
+
if (this.props.mipmaps && !textureData.hasExplicitMipChain && !shouldGenerateMipmaps) {
|
|
222
|
+
log.warn(`${this} skipping auto-generated mipmaps for compressed texture format`)();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (shouldGenerateMipmaps) {
|
|
222
226
|
this.generateMipmaps();
|
|
223
227
|
}
|
|
224
228
|
|
|
@@ -247,7 +251,7 @@ export class DynamicTexture {
|
|
|
247
251
|
if (this.device.type === 'webgl') {
|
|
248
252
|
this.texture.generateMipmapsWebGL();
|
|
249
253
|
} else if (this.device.type === 'webgpu') {
|
|
250
|
-
|
|
254
|
+
this.device.generateMipmapsWebGPU(this.texture);
|
|
251
255
|
} else {
|
|
252
256
|
log.warn(`${this} mipmaps not supported on ${this.device.type}`);
|
|
253
257
|
}
|
|
@@ -342,7 +346,7 @@ export class DynamicTexture {
|
|
|
342
346
|
}
|
|
343
347
|
|
|
344
348
|
/** Cube array: multiple cubes (faces×layers), each face may carry multiple mips */
|
|
345
|
-
|
|
349
|
+
setTextureCubeArrayData(data: TextureCubeArrayData): void {
|
|
346
350
|
if (this.texture.props.dimension !== 'cube-array') {
|
|
347
351
|
throw new Error(`${this} is not cube-array`);
|
|
348
352
|
}
|
|
@@ -367,10 +371,13 @@ export class DynamicTexture {
|
|
|
367
371
|
this.texture.copyExternalImage({image, z, mipLevel, flipY});
|
|
368
372
|
break;
|
|
369
373
|
case 'texture-data':
|
|
370
|
-
const {data} = subresource;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
+
const {data, textureFormat} = subresource;
|
|
375
|
+
if (textureFormat && textureFormat !== this.texture.format) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
`${this} mip level ${mipLevel} uses format "${textureFormat}" but texture format is "${this.texture.format}"`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
this.texture.writeData(data.data, {
|
|
374
381
|
x: 0,
|
|
375
382
|
y: 0,
|
|
376
383
|
z,
|
|
@@ -415,36 +422,190 @@ export class DynamicTexture {
|
|
|
415
422
|
};
|
|
416
423
|
}
|
|
417
424
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
return
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
425
|
+
type TextureSubresourceAnalysis = {
|
|
426
|
+
readonly subresources: TextureSubresource[];
|
|
427
|
+
readonly mipLevels: number;
|
|
428
|
+
readonly format?: TextureFormat;
|
|
429
|
+
readonly hasExplicitMipChain: boolean;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// Flatten dimension-specific texture data into one list of uploadable subresources.
|
|
433
|
+
function getTextureSubresources(props: TextureDataProps): TextureSubresource[] {
|
|
434
|
+
if (!props.data) {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
switch (props.dimension) {
|
|
439
|
+
case '1d':
|
|
440
|
+
return getTexture1DSubresources(props.data);
|
|
441
|
+
case '2d':
|
|
442
|
+
return getTexture2DSubresources(0, props.data);
|
|
443
|
+
case '3d':
|
|
444
|
+
return getTexture3DSubresources(props.data);
|
|
445
|
+
case '2d-array':
|
|
446
|
+
return getTextureArraySubresources(props.data);
|
|
447
|
+
case 'cube':
|
|
448
|
+
return getTextureCubeSubresources(props.data);
|
|
449
|
+
case 'cube-array':
|
|
450
|
+
return getTextureCubeArraySubresources(props.data);
|
|
451
|
+
default:
|
|
452
|
+
throw new Error(`Unhandled dimension ${(props as TextureDataProps).dimension}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Resolve a consistent texture format and the longest mip chain valid across all slices.
|
|
457
|
+
function analyzeTextureSubresources(
|
|
458
|
+
device: Device,
|
|
459
|
+
subresources: TextureSubresource[],
|
|
460
|
+
size: {width: number; height: number},
|
|
461
|
+
options: {format?: TextureFormat}
|
|
462
|
+
): TextureSubresourceAnalysis {
|
|
463
|
+
if (subresources.length === 0) {
|
|
464
|
+
return {
|
|
465
|
+
subresources,
|
|
466
|
+
mipLevels: 1,
|
|
467
|
+
format: options.format,
|
|
468
|
+
hasExplicitMipChain: false
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const groupedSubresources = new Map<number, TextureSubresource[]>();
|
|
473
|
+
for (const subresource of subresources) {
|
|
474
|
+
const group = groupedSubresources.get(subresource.z) ?? [];
|
|
475
|
+
group.push(subresource);
|
|
476
|
+
groupedSubresources.set(subresource.z, group);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const hasExplicitMipChain = subresources.some(subresource => subresource.mipLevel > 0);
|
|
480
|
+
let resolvedFormat = options.format;
|
|
481
|
+
let resolvedMipLevels = Number.POSITIVE_INFINITY;
|
|
482
|
+
const validSubresources: TextureSubresource[] = [];
|
|
483
|
+
|
|
484
|
+
for (const [z, sliceSubresources] of groupedSubresources) {
|
|
485
|
+
// Validate each slice independently, then keep only the mip levels that are valid everywhere.
|
|
486
|
+
const sortedSubresources = [...sliceSubresources].sort(
|
|
487
|
+
(left, right) => left.mipLevel - right.mipLevel
|
|
444
488
|
);
|
|
489
|
+
const baseLevel = sortedSubresources[0];
|
|
490
|
+
if (!baseLevel || baseLevel.mipLevel !== 0) {
|
|
491
|
+
throw new Error(`DynamicTexture: slice ${z} is missing mip level 0`);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const baseSize = getTextureSubresourceSize(device, baseLevel);
|
|
495
|
+
if (baseSize.width !== size.width || baseSize.height !== size.height) {
|
|
496
|
+
throw new Error(
|
|
497
|
+
`DynamicTexture: slice ${z} base level dimensions ${baseSize.width}x${baseSize.height} do not match expected ${size.width}x${size.height}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const baseFormat = getTextureSubresourceFormat(baseLevel);
|
|
502
|
+
if (baseFormat) {
|
|
503
|
+
if (resolvedFormat && resolvedFormat !== baseFormat) {
|
|
504
|
+
throw new Error(
|
|
505
|
+
`DynamicTexture: slice ${z} base level format "${baseFormat}" does not match texture format "${resolvedFormat}"`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
resolvedFormat = baseFormat;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const mipLevelLimit =
|
|
512
|
+
resolvedFormat && device.isTextureFormatCompressed(resolvedFormat)
|
|
513
|
+
? // Block-compressed formats cannot have mips smaller than a single compression block.
|
|
514
|
+
getMaxCompressedMipLevels(device, baseSize.width, baseSize.height, resolvedFormat)
|
|
515
|
+
: device.getMipLevelCount(baseSize.width, baseSize.height);
|
|
516
|
+
|
|
517
|
+
let validMipLevelsForSlice = 0;
|
|
518
|
+
for (
|
|
519
|
+
let expectedMipLevel = 0;
|
|
520
|
+
expectedMipLevel < sortedSubresources.length;
|
|
521
|
+
expectedMipLevel++
|
|
522
|
+
) {
|
|
523
|
+
const subresource = sortedSubresources[expectedMipLevel];
|
|
524
|
+
// Stop at the first gap so callers can provide extra trailing data without breaking creation.
|
|
525
|
+
if (!subresource || subresource.mipLevel !== expectedMipLevel) {
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
if (expectedMipLevel >= mipLevelLimit) {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const subresourceSize = getTextureSubresourceSize(device, subresource);
|
|
533
|
+
const expectedWidth = Math.max(1, baseSize.width >> expectedMipLevel);
|
|
534
|
+
const expectedHeight = Math.max(1, baseSize.height >> expectedMipLevel);
|
|
535
|
+
if (subresourceSize.width !== expectedWidth || subresourceSize.height !== expectedHeight) {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const subresourceFormat = getTextureSubresourceFormat(subresource);
|
|
540
|
+
if (subresourceFormat) {
|
|
541
|
+
if (!resolvedFormat) {
|
|
542
|
+
resolvedFormat = subresourceFormat;
|
|
543
|
+
}
|
|
544
|
+
// Later mip levels must stay on the same format as the validated base level.
|
|
545
|
+
if (subresourceFormat !== resolvedFormat) {
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
validMipLevelsForSlice++;
|
|
551
|
+
validSubresources.push(subresource);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
resolvedMipLevels = Math.min(resolvedMipLevels, validMipLevelsForSlice);
|
|
445
555
|
}
|
|
446
556
|
|
|
447
|
-
|
|
557
|
+
const mipLevels = Number.isFinite(resolvedMipLevels) ? Math.max(1, resolvedMipLevels) : 1;
|
|
558
|
+
|
|
559
|
+
return {
|
|
560
|
+
// Keep every slice trimmed to the same mip count so the texture shape stays internally consistent.
|
|
561
|
+
subresources: validSubresources.filter(subresource => subresource.mipLevel < mipLevels),
|
|
562
|
+
mipLevels,
|
|
563
|
+
format: resolvedFormat,
|
|
564
|
+
hasExplicitMipChain
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Read the per-level format using the transitional textureFormat -> format fallback rules.
|
|
569
|
+
function getTextureSubresourceFormat(subresource: TextureSubresource): TextureFormat | undefined {
|
|
570
|
+
if (subresource.type !== 'texture-data') {
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
return subresource.textureFormat ?? resolveTextureImageFormat(subresource.data);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Resolve dimensions from either raw bytes or external-image subresources.
|
|
577
|
+
function getTextureSubresourceSize(
|
|
578
|
+
device: Device,
|
|
579
|
+
subresource: TextureSubresource
|
|
580
|
+
): {width: number; height: number} {
|
|
581
|
+
switch (subresource.type) {
|
|
582
|
+
case 'external-image':
|
|
583
|
+
return device.getExternalImageSize(subresource.image);
|
|
584
|
+
case 'texture-data':
|
|
585
|
+
return {width: subresource.data.width, height: subresource.data.height};
|
|
586
|
+
default:
|
|
587
|
+
throw new Error('Unsupported texture subresource');
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Count the mip levels that stay at or above one compression block in each dimension.
|
|
592
|
+
function getMaxCompressedMipLevels(
|
|
593
|
+
device: Device,
|
|
594
|
+
baseWidth: number,
|
|
595
|
+
baseHeight: number,
|
|
596
|
+
format: TextureFormat
|
|
597
|
+
): number {
|
|
598
|
+
const {blockWidth = 1, blockHeight = 1} = device.getTextureFormatInfo(format);
|
|
599
|
+
let mipLevels = 1;
|
|
600
|
+
for (let mipLevel = 1; ; mipLevel++) {
|
|
601
|
+
const width = Math.max(1, baseWidth >> mipLevel);
|
|
602
|
+
const height = Math.max(1, baseHeight >> mipLevel);
|
|
603
|
+
if (width < blockWidth || height < blockHeight) {
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
mipLevels++;
|
|
607
|
+
}
|
|
608
|
+
return mipLevels;
|
|
448
609
|
}
|
|
449
610
|
|
|
450
611
|
// HELPERS
|
|
@@ -9,6 +9,8 @@ export type TextureImageSource = ExternalImage;
|
|
|
9
9
|
* additional optional fields can describe compressed texture data.
|
|
10
10
|
*/
|
|
11
11
|
export type TextureImageData = {
|
|
12
|
+
/** Preferred WebGPU style format string. */
|
|
13
|
+
textureFormat?: TextureFormat;
|
|
12
14
|
/** WebGPU style format string. Defaults to 'rgba8unorm' */
|
|
13
15
|
format?: TextureFormat;
|
|
14
16
|
/** Typed Array with the bytes of the image. @note beware row byte alignment requirements */
|
|
@@ -104,6 +106,7 @@ export type TextureSubresource = {
|
|
|
104
106
|
| {
|
|
105
107
|
type: 'texture-data';
|
|
106
108
|
data: TextureImageData;
|
|
109
|
+
textureFormat?: TextureFormat;
|
|
107
110
|
}
|
|
108
111
|
);
|
|
109
112
|
|
|
@@ -184,6 +187,16 @@ function isTextureImageData(data: TextureMipLevelData): data is TextureImageData
|
|
|
184
187
|
);
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
export function resolveTextureImageFormat(data: TextureImageData): TextureFormat | undefined {
|
|
191
|
+
const {textureFormat, format} = data;
|
|
192
|
+
if (textureFormat && format && textureFormat !== format) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Conflicting texture formats "${textureFormat}" and "${format}" provided for the same mip level`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return textureFormat ?? format;
|
|
198
|
+
}
|
|
199
|
+
|
|
187
200
|
/** Resolve size for a single mip-level datum */
|
|
188
201
|
// function getTextureMipLevelSizeFromData(data: TextureMipLevelData): {
|
|
189
202
|
// width: number;
|
|
@@ -249,6 +262,7 @@ export function getTexture2DSubresources(
|
|
|
249
262
|
subresources.push({
|
|
250
263
|
type: 'texture-data',
|
|
251
264
|
data: imageData,
|
|
265
|
+
textureFormat: resolveTextureImageFormat(imageData),
|
|
252
266
|
z,
|
|
253
267
|
mipLevel
|
|
254
268
|
});
|