@luma.gl/core 9.3.0-alpha.2 → 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.
Files changed (121) hide show
  1. package/dist/adapter/canvas-context.d.ts +6 -181
  2. package/dist/adapter/canvas-context.d.ts.map +1 -1
  3. package/dist/adapter/canvas-context.js +5 -450
  4. package/dist/adapter/canvas-context.js.map +1 -1
  5. package/dist/adapter/canvas-observer.d.ts +32 -0
  6. package/dist/adapter/canvas-observer.d.ts.map +1 -0
  7. package/dist/adapter/canvas-observer.js +90 -0
  8. package/dist/adapter/canvas-observer.js.map +1 -0
  9. package/dist/adapter/canvas-surface.d.ts +150 -0
  10. package/dist/adapter/canvas-surface.d.ts.map +1 -0
  11. package/dist/adapter/canvas-surface.js +392 -0
  12. package/dist/adapter/canvas-surface.js.map +1 -0
  13. package/dist/adapter/device.d.ts +64 -9
  14. package/dist/adapter/device.d.ts.map +1 -1
  15. package/dist/adapter/device.js +108 -4
  16. package/dist/adapter/device.js.map +1 -1
  17. package/dist/adapter/luma.js +1 -1
  18. package/dist/adapter/presentation-context.d.ts +11 -0
  19. package/dist/adapter/presentation-context.d.ts.map +1 -0
  20. package/dist/adapter/presentation-context.js +12 -0
  21. package/dist/adapter/presentation-context.js.map +1 -0
  22. package/dist/adapter/resources/buffer.d.ts +1 -1
  23. package/dist/adapter/resources/buffer.d.ts.map +1 -1
  24. package/dist/adapter/resources/buffer.js +14 -6
  25. package/dist/adapter/resources/buffer.js.map +1 -1
  26. package/dist/adapter/resources/command-encoder.d.ts +27 -6
  27. package/dist/adapter/resources/command-encoder.d.ts.map +1 -1
  28. package/dist/adapter/resources/command-encoder.js +65 -1
  29. package/dist/adapter/resources/command-encoder.js.map +1 -1
  30. package/dist/adapter/resources/fence.d.ts +1 -1
  31. package/dist/adapter/resources/fence.d.ts.map +1 -1
  32. package/dist/adapter/resources/fence.js +3 -1
  33. package/dist/adapter/resources/fence.js.map +1 -1
  34. package/dist/adapter/resources/framebuffer.d.ts.map +1 -1
  35. package/dist/adapter/resources/framebuffer.js +9 -11
  36. package/dist/adapter/resources/framebuffer.js.map +1 -1
  37. package/dist/adapter/resources/query-set.d.ts +17 -1
  38. package/dist/adapter/resources/query-set.d.ts.map +1 -1
  39. package/dist/adapter/resources/query-set.js.map +1 -1
  40. package/dist/adapter/resources/render-pipeline.d.ts +19 -7
  41. package/dist/adapter/resources/render-pipeline.d.ts.map +1 -1
  42. package/dist/adapter/resources/render-pipeline.js +20 -2
  43. package/dist/adapter/resources/render-pipeline.js.map +1 -1
  44. package/dist/adapter/resources/resource.d.ts +8 -0
  45. package/dist/adapter/resources/resource.d.ts.map +1 -1
  46. package/dist/adapter/resources/resource.js +240 -14
  47. package/dist/adapter/resources/resource.js.map +1 -1
  48. package/dist/adapter/resources/shader.js +27 -25
  49. package/dist/adapter/resources/shader.js.map +1 -1
  50. package/dist/adapter/resources/shared-render-pipeline.d.ts +22 -0
  51. package/dist/adapter/resources/shared-render-pipeline.d.ts.map +1 -0
  52. package/dist/adapter/resources/shared-render-pipeline.js +25 -0
  53. package/dist/adapter/resources/shared-render-pipeline.js.map +1 -0
  54. package/dist/adapter/resources/texture.d.ts +78 -12
  55. package/dist/adapter/resources/texture.d.ts.map +1 -1
  56. package/dist/adapter/resources/texture.js +182 -30
  57. package/dist/adapter/resources/texture.js.map +1 -1
  58. package/dist/adapter-utils/format-compiler-log.d.ts.map +1 -1
  59. package/dist/adapter-utils/format-compiler-log.js +23 -15
  60. package/dist/adapter-utils/format-compiler-log.js.map +1 -1
  61. package/dist/dist.dev.js +1265 -362
  62. package/dist/dist.min.js +10 -9
  63. package/dist/index.cjs +1027 -243
  64. package/dist/index.cjs.map +4 -4
  65. package/dist/index.d.ts +5 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +3 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/shadertypes/data-types/decode-shader-types.d.ts +2 -2
  70. package/dist/shadertypes/data-types/decode-shader-types.d.ts.map +1 -1
  71. package/dist/shadertypes/data-types/decode-shader-types.js +11 -2
  72. package/dist/shadertypes/data-types/decode-shader-types.js.map +1 -1
  73. package/dist/shadertypes/textures/pixel-utils.js +4 -4
  74. package/dist/shadertypes/textures/pixel-utils.js.map +1 -1
  75. package/dist/shadertypes/textures/texture-format-decoder.d.ts.map +1 -1
  76. package/dist/shadertypes/textures/texture-format-decoder.js +53 -8
  77. package/dist/shadertypes/textures/texture-format-decoder.js.map +1 -1
  78. package/dist/shadertypes/textures/texture-format-table.d.ts.map +1 -1
  79. package/dist/shadertypes/textures/texture-format-table.js +10 -9
  80. package/dist/shadertypes/textures/texture-format-table.js.map +1 -1
  81. package/dist/shadertypes/textures/texture-formats.d.ts +5 -2
  82. package/dist/shadertypes/textures/texture-formats.d.ts.map +1 -1
  83. package/dist/shadertypes/textures/texture-formats.js.map +1 -1
  84. package/dist/shadertypes/textures/texture-layout.d.ts +1 -1
  85. package/dist/utils/array-equal.d.ts +1 -1
  86. package/dist/utils/array-equal.d.ts.map +1 -1
  87. package/dist/utils/array-equal.js +15 -9
  88. package/dist/utils/array-equal.js.map +1 -1
  89. package/dist/utils/assert.d.ts +5 -0
  90. package/dist/utils/assert.d.ts.map +1 -0
  91. package/dist/utils/assert.js +17 -0
  92. package/dist/utils/assert.js.map +1 -0
  93. package/dist/utils/stats-manager.d.ts.map +1 -1
  94. package/dist/utils/stats-manager.js +61 -1
  95. package/dist/utils/stats-manager.js.map +1 -1
  96. package/package.json +6 -6
  97. package/src/adapter/canvas-context.ts +7 -590
  98. package/src/adapter/canvas-observer.ts +130 -0
  99. package/src/adapter/canvas-surface.ts +521 -0
  100. package/src/adapter/device.ts +174 -13
  101. package/src/adapter/presentation-context.ts +16 -0
  102. package/src/adapter/resources/buffer.ts +13 -5
  103. package/src/adapter/resources/command-encoder.ts +96 -7
  104. package/src/adapter/resources/fence.ts +3 -1
  105. package/src/adapter/resources/framebuffer.ts +9 -11
  106. package/src/adapter/resources/query-set.ts +17 -1
  107. package/src/adapter/resources/render-pipeline.ts +42 -13
  108. package/src/adapter/resources/resource.ts +284 -14
  109. package/src/adapter/resources/shader.ts +28 -28
  110. package/src/adapter/resources/shared-render-pipeline.ts +40 -0
  111. package/src/adapter/resources/texture.ts +269 -40
  112. package/src/adapter-utils/format-compiler-log.ts +23 -15
  113. package/src/index.ts +8 -0
  114. package/src/shadertypes/data-types/decode-shader-types.ts +13 -4
  115. package/src/shadertypes/textures/pixel-utils.ts +4 -4
  116. package/src/shadertypes/textures/texture-format-decoder.ts +73 -8
  117. package/src/shadertypes/textures/texture-format-table.ts +10 -9
  118. package/src/shadertypes/textures/texture-formats.ts +6 -1
  119. package/src/utils/array-equal.ts +21 -9
  120. package/src/utils/assert.ts +18 -0
  121. package/src/utils/stats-manager.ts +76 -2
@@ -84,11 +84,20 @@ function computeTextureMemoryLayout({
84
84
  depth,
85
85
  byteAlignment
86
86
  }: TextureMemoryLayoutOptions): TextureMemoryLayout {
87
- const {bytesPerPixel} = textureFormatDecoder.getInfo(format);
88
- // WebGPU requires bytesPerRow to be a multiple of 256.
89
- const unpaddedBytesPerRow = width * bytesPerPixel;
87
+ const formatInfo = textureFormatDecoder.getInfo(format);
88
+ const {
89
+ bytesPerPixel,
90
+ bytesPerBlock = bytesPerPixel,
91
+ blockWidth = 1,
92
+ blockHeight = 1,
93
+ compressed = false
94
+ } = formatInfo;
95
+ const blockColumns = compressed ? Math.ceil(width / blockWidth) : width;
96
+ const blockRows = compressed ? Math.ceil(height / blockHeight) : height;
97
+ // byteAlignment comes from the backend/call site. WebGPU buffer copies use 256, queue.writeTexture uses 1.
98
+ const unpaddedBytesPerRow = blockColumns * bytesPerBlock;
90
99
  const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment;
91
- const rowsPerImage = height;
100
+ const rowsPerImage = blockRows;
92
101
  const byteLength = bytesPerRow * rowsPerImage * depth;
93
102
 
94
103
  return {
@@ -120,9 +129,10 @@ function getTextureFormatCapabilities(format: TextureFormat): TextureFormatCapab
120
129
  const isSigned = formatInfo?.signed;
121
130
  const isInteger = formatInfo?.integer;
122
131
  const isWebGLSpecific = formatInfo?.webgl;
132
+ const isCompressed = Boolean(formatInfo?.compressed);
123
133
 
124
- // signed formats are not renderable
125
- formatCapabilities.render &&= !isSigned;
134
+ // Only uncompressed color formats can be used as color-renderable attachments.
135
+ formatCapabilities.render &&= !isDepthStencil && !isCompressed;
126
136
  // signed and integer formats are not filterable
127
137
  formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
128
138
 
@@ -143,6 +153,7 @@ export function getTextureFormatInfo(format: TextureFormat): TextureFormatInfo {
143
153
  formatInfo.bytesPerPixel = 1;
144
154
  formatInfo.srgb = false;
145
155
  formatInfo.compressed = true;
156
+ formatInfo.bytesPerBlock = getCompressedTextureBlockByteLength(format);
146
157
 
147
158
  const blockSize = getCompressedTextureBlockSize(format);
148
159
  if (blockSize) {
@@ -158,7 +169,7 @@ export function getTextureFormatInfo(format: TextureFormat): TextureFormatInfo {
158
169
  const dataType = `${type}${length}` as NormalizedDataType;
159
170
  const decodedType = getDataTypeInfo(dataType);
160
171
  const bits = decodedType.byteLength * 8;
161
- const components = channels.length as 1 | 2 | 3 | 4;
172
+ const components = (channels?.length ?? 1) as 1 | 2 | 3 | 4;
162
173
  const bitsPerChannel: [number, number, number, number] = [
163
174
  bits,
164
175
  components >= 2 ? bits : 0,
@@ -176,7 +187,7 @@ export function getTextureFormatInfo(format: TextureFormat): TextureFormatInfo {
176
187
  signed: decodedType.signed,
177
188
  normalized: decodedType.normalized,
178
189
  bitsPerChannel,
179
- bytesPerPixel: decodedType.byteLength * channels.length,
190
+ bytesPerPixel: decodedType.byteLength * components,
180
191
  packed: formatInfo.packed,
181
192
  srgb: formatInfo.srgb
182
193
  };
@@ -245,9 +256,63 @@ function getCompressedTextureBlockSize(
245
256
  const [, blockWidth, blockHeight] = matches;
246
257
  return {blockWidth: Number(blockWidth), blockHeight: Number(blockHeight)};
247
258
  }
259
+
260
+ if (
261
+ format.startsWith('bc') ||
262
+ format.startsWith('etc1') ||
263
+ format.startsWith('etc2') ||
264
+ format.startsWith('eac') ||
265
+ format.startsWith('atc')
266
+ ) {
267
+ return {blockWidth: 4, blockHeight: 4};
268
+ }
269
+
270
+ if (format.startsWith('pvrtc-rgb4') || format.startsWith('pvrtc-rgba4')) {
271
+ return {blockWidth: 4, blockHeight: 4};
272
+ }
273
+
274
+ if (format.startsWith('pvrtc-rgb2') || format.startsWith('pvrtc-rgba2')) {
275
+ return {blockWidth: 8, blockHeight: 4};
276
+ }
277
+
248
278
  return null;
249
279
  }
250
280
 
281
+ function getCompressedTextureBlockByteLength(format: TextureFormatCompressed): number {
282
+ if (
283
+ format.startsWith('bc1') ||
284
+ format.startsWith('bc4') ||
285
+ format.startsWith('etc1') ||
286
+ format.startsWith('etc2-rgb8') ||
287
+ format.startsWith('etc2-rgb8a1') ||
288
+ format.startsWith('eac-r11') ||
289
+ format === 'atc-rgb-unorm-webgl'
290
+ ) {
291
+ return 8;
292
+ }
293
+
294
+ if (
295
+ format.startsWith('bc2') ||
296
+ format.startsWith('bc3') ||
297
+ format.startsWith('bc5') ||
298
+ format.startsWith('bc6h') ||
299
+ format.startsWith('bc7') ||
300
+ format.startsWith('etc2-rgba8') ||
301
+ format.startsWith('eac-rg11') ||
302
+ format.startsWith('astc') ||
303
+ format === 'atc-rgba-unorm-webgl' ||
304
+ format === 'atc-rgbai-unorm-webgl'
305
+ ) {
306
+ return 16;
307
+ }
308
+
309
+ if (format.startsWith('pvrtc')) {
310
+ return 8;
311
+ }
312
+
313
+ return 16;
314
+ }
315
+
251
316
  /*
252
317
  'r8unorm': {s: "float"}, // ✓ ✓ ✓ },
253
318
  'r8snorm': {s: "float"}, // ✓ },
@@ -24,6 +24,7 @@ const float32_renderable: TextureFeature = 'float32-renderable-webgl';
24
24
  const float16_renderable: TextureFeature = 'float16-renderable-webgl';
25
25
  const rgb9e5ufloat_renderable: TextureFeature = 'rgb9e5ufloat-renderable-webgl';
26
26
  const snorm8_renderable: TextureFeature = 'snorm8-renderable-webgl';
27
+ const norm16_webgl: TextureFeature = 'norm16-webgl';
27
28
  const norm16_renderable: TextureFeature = 'norm16-renderable-webgl';
28
29
  const snorm16_renderable: TextureFeature = 'snorm16-renderable-webgl';
29
30
 
@@ -93,15 +94,15 @@ const TEXTURE_FORMAT_COLOR_DEPTH_TABLE: Readonly<Record<TextureFormatColorUncomp
93
94
  'bgra8unorm-srgb': {},
94
95
 
95
96
 
96
- 'r16unorm': {f: norm16_renderable},
97
- 'rg16unorm': {render: norm16_renderable},
98
- 'rgb16unorm-webgl': {f: norm16_renderable}, // rgb not renderable
99
- 'rgba16unorm': {render: norm16_renderable},
97
+ 'r16unorm': {f: norm16_webgl, render: norm16_renderable},
98
+ 'rg16unorm': {f: norm16_webgl, render: norm16_renderable},
99
+ 'rgb16unorm-webgl': {f: norm16_webgl, render: false}, // rgb not renderable
100
+ 'rgba16unorm': {f: norm16_webgl, render: norm16_renderable},
100
101
 
101
- 'r16snorm': {f: snorm16_renderable},
102
- 'rg16snorm': {render: snorm16_renderable},
103
- 'rgb16snorm-webgl': {f: norm16_renderable}, // rgb not renderable
104
- 'rgba16snorm': {render: snorm16_renderable},
102
+ 'r16snorm': {f: norm16_webgl, render: snorm16_renderable},
103
+ 'rg16snorm': {f: norm16_webgl, render: snorm16_renderable},
104
+ 'rgb16snorm-webgl': {f: norm16_webgl, render: false}, // rgb not renderable
105
+ 'rgba16snorm': {f: norm16_webgl, render: snorm16_renderable},
105
106
 
106
107
  'r16uint': {},
107
108
  'rg16uint': {},
@@ -225,7 +226,7 @@ const TEXTURE_FORMAT_COMPRESSED_TABLE: Readonly<Record<TextureFormatCompressed,
225
226
 
226
227
  'pvrtc-rgb4unorm-webgl': {f: texture_compression_pvrtc_webgl},
227
228
  'pvrtc-rgba4unorm-webgl': {f: texture_compression_pvrtc_webgl},
228
- 'pvrtc-rbg2unorm-webgl': {f: texture_compression_pvrtc_webgl},
229
+ 'pvrtc-rgb2unorm-webgl': {f: texture_compression_pvrtc_webgl},
229
230
  'pvrtc-rgba2unorm-webgl': {f: texture_compression_pvrtc_webgl},
230
231
 
231
232
  // WEBGL_compressed_texture_etc1
@@ -18,6 +18,8 @@ export type TextureFormatInfo = {
18
18
  dataType?: NormalizedDataType;
19
19
  /** Number of bytes per pixel */
20
20
  bytesPerPixel: number;
21
+ /** Compressed formats only: Number of bytes per block */
22
+ bytesPerBlock?: number;
21
23
  /** Number of bits per channel (may be unreliable for packed formats) */
22
24
  bitsPerChannel: [number, number, number, number];
23
25
  /** If this is a packed data type */
@@ -116,6 +118,7 @@ export type TextureFeature =
116
118
  | 'float16-renderable-webgl'
117
119
  | 'rgb9e5ufloat-renderable-webgl'
118
120
  | 'snorm8-renderable-webgl'
121
+ | 'norm16-webgl'
119
122
  | 'norm16-renderable-webgl'
120
123
  | 'snorm16-renderable-webgl'
121
124
  | 'float32-filterable'
@@ -205,7 +208,7 @@ export type TextureFormatCompressed =
205
208
  | 'bc1-rgb-unorm-srgb-webgl'
206
209
  | 'pvrtc-rgb4unorm-webgl'
207
210
  | 'pvrtc-rgba4unorm-webgl'
208
- | 'pvrtc-rbg2unorm-webgl'
211
+ | 'pvrtc-rgb2unorm-webgl'
209
212
  | 'pvrtc-rgba2unorm-webgl'
210
213
  | 'etc1-rbg-unorm-webgl'
211
214
  | 'atc-rgb-unorm-webgl'
@@ -274,6 +277,8 @@ export type TextureFormatCompressed =
274
277
  | 'astc-12x12-unorm'
275
278
  | 'astc-12x12-unorm-srgb';
276
279
 
280
+ export type CompressedTextureFormat = TextureFormatCompressed;
281
+
277
282
  // Texture format helper types
278
283
 
279
284
  export type TextureFormatTypedArray<T extends TextureFormat> = DataTypeArray<
@@ -4,23 +4,35 @@
4
4
 
5
5
  import {isNumberArray} from './is-array';
6
6
 
7
- /** Test if two arrays are deep equal, with a length limit that defaults to 16 */
7
+ const MAX_ELEMENTWISE_ARRAY_COMPARE_LENGTH = 128;
8
+
9
+ /** Test if two arrays are deep equal, with a small-array length limit that defaults to 16 */
8
10
  export function arrayEqual(a: unknown, b: unknown, limit: number = 16) {
9
- if (a !== b) {
10
- return false;
11
+ if (a === b) {
12
+ return true;
11
13
  }
14
+
12
15
  const arrayA = a;
13
16
  const arrayB = b;
14
- if (!isNumberArray(arrayA)) {
17
+ if (!isNumberArray(arrayA) || !isNumberArray(arrayB)) {
18
+ return false;
19
+ }
20
+
21
+ if (arrayA.length !== arrayB.length) {
15
22
  return false;
16
23
  }
17
- if (isNumberArray(arrayB) && arrayA.length === arrayB.length) {
18
- for (let i = 0; i < arrayA.length; ++i) {
19
- if (arrayB[i] !== arrayA[i]) {
20
- return false;
21
- }
24
+
25
+ const maxCompareLength = Math.min(limit, MAX_ELEMENTWISE_ARRAY_COMPARE_LENGTH);
26
+ if (arrayA.length > maxCompareLength) {
27
+ return false;
28
+ }
29
+
30
+ for (let i = 0; i < arrayA.length; ++i) {
31
+ if (arrayB[i] !== arrayA[i]) {
32
+ return false;
22
33
  }
23
34
  }
35
+
24
36
  return true;
25
37
  }
26
38
 
@@ -0,0 +1,18 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** Throws if condition is true and narrows type */
6
+ export function assert(condition: unknown, message?: string): asserts condition {
7
+ if (!condition) {
8
+ const error = new Error(message ?? 'luma.gl assertion failed.');
9
+ Error.captureStackTrace?.(error, assert);
10
+ throw error;
11
+ }
12
+ }
13
+
14
+ /** Throws if value is not defined, narrows type */
15
+ export function assertDefined<T>(value: T | undefined, message?: string): T {
16
+ assert(value, message);
17
+ return value;
18
+ }
@@ -2,7 +2,29 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Stats} from '@probe.gl/stats';
5
+ import {Stat, Stats} from '@probe.gl/stats';
6
+
7
+ const GPU_TIME_AND_MEMORY_STATS = 'GPU Time and Memory';
8
+ const GPU_TIME_AND_MEMORY_STAT_ORDER = [
9
+ 'Adapter',
10
+ 'GPU',
11
+ 'GPU Type',
12
+ 'GPU Backend',
13
+ 'Frame Rate',
14
+ 'CPU Time',
15
+ 'GPU Time',
16
+ 'GPU Memory',
17
+ 'Buffer Memory',
18
+ 'Texture Memory',
19
+ 'Referenced Buffer Memory',
20
+ 'Referenced Texture Memory',
21
+ 'Swap Chain Texture'
22
+ ] as const;
23
+ const ORDERED_STATS_CACHE = new WeakMap<
24
+ Stats,
25
+ {orderedStatNames: readonly string[]; statCount: number}
26
+ >();
27
+ const ORDERED_STAT_NAME_SET_CACHE = new WeakMap<readonly string[], Set<string>>();
6
28
 
7
29
  /**
8
30
  * Helper class managing a collection of probe.gl stats objects
@@ -19,9 +41,61 @@ export class StatsManager {
19
41
  this.stats.set(name, new Stats({id: name}));
20
42
  }
21
43
 
22
- return this.stats.get(name);
44
+ const stats = this.stats.get(name);
45
+ if (name === GPU_TIME_AND_MEMORY_STATS) {
46
+ initializeStats(stats, GPU_TIME_AND_MEMORY_STAT_ORDER);
47
+ }
48
+
49
+ return stats;
23
50
  }
24
51
  }
25
52
 
26
53
  /** Global stats for all luma.gl devices */
27
54
  export const lumaStats: StatsManager = new StatsManager();
55
+
56
+ function initializeStats(stats: Stats, orderedStatNames: readonly string[]): void {
57
+ const statsMap = stats.stats;
58
+ let addedOrderedStat = false;
59
+ for (const statName of orderedStatNames) {
60
+ if (!statsMap[statName]) {
61
+ stats.get(statName);
62
+ addedOrderedStat = true;
63
+ }
64
+ }
65
+
66
+ const statCount = Object.keys(statsMap).length;
67
+ const cachedStats = ORDERED_STATS_CACHE.get(stats);
68
+ if (
69
+ !addedOrderedStat &&
70
+ cachedStats?.orderedStatNames === orderedStatNames &&
71
+ cachedStats.statCount === statCount
72
+ ) {
73
+ return;
74
+ }
75
+
76
+ const reorderedStats: Record<string, Stat> = {};
77
+ let orderedStatNamesSet = ORDERED_STAT_NAME_SET_CACHE.get(orderedStatNames);
78
+ if (!orderedStatNamesSet) {
79
+ orderedStatNamesSet = new Set(orderedStatNames);
80
+ ORDERED_STAT_NAME_SET_CACHE.set(orderedStatNames, orderedStatNamesSet);
81
+ }
82
+
83
+ for (const statName of orderedStatNames) {
84
+ if (statsMap[statName]) {
85
+ reorderedStats[statName] = statsMap[statName];
86
+ }
87
+ }
88
+
89
+ for (const [statName, stat] of Object.entries(statsMap)) {
90
+ if (!orderedStatNamesSet.has(statName)) {
91
+ reorderedStats[statName] = stat;
92
+ }
93
+ }
94
+
95
+ for (const statName of Object.keys(statsMap)) {
96
+ delete statsMap[statName];
97
+ }
98
+
99
+ Object.assign(statsMap, reorderedStats);
100
+ ORDERED_STATS_CACHE.set(stats, {orderedStatNames, statCount});
101
+ }