@woosh/meep-engine 3.14.1 → 3.14.2

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 (220) hide show
  1. package/README.md +31 -24
  2. package/package.json +1 -1
  3. package/src/avif/native/DECISIONS.md +544 -536
  4. package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -1
  5. package/src/avif/native/heif/parse_avif_file.js +384 -383
  6. package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -1
  7. package/src/avif/native/heif/parse_image_grid.js +49 -48
  8. package/src/basis/BasisBitReader.d.ts +77 -0
  9. package/src/basis/BasisBitReader.d.ts.map +1 -0
  10. package/src/basis/BasisBitReader.js +231 -0
  11. package/src/basis/BasisHuffmanTable.d.ts +53 -0
  12. package/src/basis/BasisHuffmanTable.d.ts.map +1 -0
  13. package/src/basis/BasisHuffmanTable.js +263 -0
  14. package/src/basis/basis_read_huffman_table.d.ts +20 -0
  15. package/src/basis/basis_read_huffman_table.d.ts.map +1 -0
  16. package/src/basis/basis_read_huffman_table.js +187 -0
  17. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts +50 -0
  18. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts.map +1 -0
  19. package/src/basis/etc1s/Etc1sSelectorHistory.js +89 -0
  20. package/src/basis/etc1s/etc1s_block_colors.d.ts +14 -0
  21. package/src/basis/etc1s/etc1s_block_colors.d.ts.map +1 -0
  22. package/src/basis/etc1s/etc1s_block_colors.js +75 -0
  23. package/src/basis/etc1s/etc1s_decode_slice.d.ts +57 -0
  24. package/src/basis/etc1s/etc1s_decode_slice.d.ts.map +1 -0
  25. package/src/basis/etc1s/etc1s_decode_slice.js +258 -0
  26. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts +48 -0
  27. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts.map +1 -0
  28. package/src/basis/etc1s/etc1s_read_endpoint_codebook.js +111 -0
  29. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts +42 -0
  30. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts.map +1 -0
  31. package/src/basis/etc1s/etc1s_read_selector_codebook.js +127 -0
  32. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts +54 -0
  33. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts.map +1 -0
  34. package/src/basis/etc1s/etc1s_read_slice_tables.js +47 -0
  35. package/src/basis/transcode/build_range_plan.js +54 -0
  36. package/src/basis/transcode/etc1s_to_bc1.d.ts +24 -0
  37. package/src/basis/transcode/etc1s_to_bc1.d.ts.map +1 -0
  38. package/src/basis/transcode/etc1s_to_bc1.js +205 -0
  39. package/src/basis/transcode/etc1s_to_bc7.d.ts +30 -0
  40. package/src/basis/transcode/etc1s_to_bc7.d.ts.map +1 -0
  41. package/src/basis/transcode/etc1s_to_bc7.js +496 -0
  42. package/src/basis/transcode/fit_endpoints.d.ts +59 -0
  43. package/src/basis/transcode/fit_endpoints.d.ts.map +1 -0
  44. package/src/basis/transcode/fit_endpoints.js +226 -0
  45. package/src/{avif/native/isobmff → core/binary}/ByteCursor.d.ts +19 -6
  46. package/src/core/binary/ByteCursor.d.ts.map +1 -0
  47. package/src/{avif/native/isobmff → core/binary}/ByteCursor.js +41 -14
  48. package/src/engine/EngineHarness.js +1 -1
  49. package/src/engine/asset/codec/Codec.d.ts.map +1 -0
  50. package/src/engine/asset/codec/CodecWithFallback.d.ts.map +1 -0
  51. package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.js +2 -2
  52. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +4 -4
  53. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts +1 -1
  54. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts.map +1 -1
  55. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
  56. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts +1 -1
  57. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts.map +1 -1
  58. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.js +1 -1
  59. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts +1 -1
  60. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts.map +1 -1
  61. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +1 -1
  62. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  63. package/src/engine/graphics/texture/atlas/TextureAtlas.js +13 -0
  64. package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
  65. package/src/engine/physics/broadphase/generate_pairs.js +7 -1
  66. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  67. package/src/ktx2/KTX2_IDENTIFIER.d.ts +14 -0
  68. package/src/ktx2/KTX2_IDENTIFIER.d.ts.map +1 -0
  69. package/src/ktx2/KTX2_IDENTIFIER.js +15 -0
  70. package/src/ktx2/KhrDfModel.d.ts +35 -0
  71. package/src/ktx2/KhrDfModel.d.ts.map +1 -0
  72. package/src/ktx2/KhrDfModel.js +49 -0
  73. package/src/ktx2/SupercompressionScheme.d.ts +16 -0
  74. package/src/ktx2/SupercompressionScheme.d.ts.map +1 -0
  75. package/src/ktx2/SupercompressionScheme.js +23 -0
  76. package/src/ktx2/ktx2_read.d.ts +71 -0
  77. package/src/ktx2/ktx2_read.d.ts.map +1 -0
  78. package/src/ktx2/ktx2_read.js +581 -0
  79. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts +27 -0
  80. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts.map +1 -0
  81. package/src/ktx2/ktx2_read_basis_lz_global_data.js +189 -0
  82. package/src/ktx2/vk_format_to_texture_format.d.ts +55 -0
  83. package/src/ktx2/vk_format_to_texture_format.d.ts.map +1 -0
  84. package/src/ktx2/vk_format_to_texture_format.js +290 -0
  85. package/src/shade/descriptor/GPUTextureUsage.d.ts +23 -0
  86. package/src/shade/descriptor/GPUTextureUsage.d.ts.map +1 -0
  87. package/src/shade/descriptor/GPUTextureUsage.js +24 -0
  88. package/src/shade/descriptor/WebGPUExtensionType.d.ts +3 -0
  89. package/src/shade/descriptor/WebGPUExtensionType.js +26 -0
  90. package/src/shade/descriptor/texture/TextureDescriptor.d.ts +2 -6
  91. package/src/shade/descriptor/texture/TextureDescriptor.d.ts.map +1 -1
  92. package/src/shade/descriptor/texture/TextureDescriptor.js +24 -40
  93. package/src/shade/descriptor/texture/format/TextureFormat.d.ts +125 -0
  94. package/src/shade/descriptor/texture/format/TextureFormat.d.ts.map +1 -0
  95. package/src/shade/descriptor/texture/format/TextureFormat.js +146 -0
  96. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts +60 -0
  97. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts.map +1 -1
  98. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +102 -0
  99. package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
  100. package/src/shade/device/graph/TextureResourceDescriptor.js +160 -155
  101. package/src/shade/device/mock/SoftwareGPUQueue.d.ts +11 -4
  102. package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
  103. package/src/shade/device/mock/SoftwareGPUQueue.js +31 -37
  104. package/src/shade/device/mock/SoftwareGPUTexture.d.ts.map +1 -1
  105. package/src/shade/device/mock/SoftwareGPUTexture.js +8 -4
  106. package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts.map +1 -1
  107. package/src/shade/device/timing/profile/frame_graph_extract_topology.js +164 -174
  108. package/src/shade/playground/ground_seam/README.md +235 -0
  109. package/src/shade/playground/ground_seam/capture_scene_color.d.ts +45 -0
  110. package/src/shade/playground/ground_seam/capture_scene_color.d.ts.map +1 -0
  111. package/src/shade/playground/ground_seam/capture_scene_color.js +242 -0
  112. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts +11 -0
  113. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts.map +1 -0
  114. package/src/shade/playground/ground_seam/depth_step_across_seam.js +185 -0
  115. package/src/shade/playground/ground_seam/index.html +195 -0
  116. package/src/shade/playground/ground_seam/main.d.ts +2 -0
  117. package/src/shade/playground/ground_seam/main.d.ts.map +1 -0
  118. package/src/shade/playground/ground_seam/main.js +879 -0
  119. package/src/shade/playground/ground_seam/make_grid_texture.d.ts +29 -0
  120. package/src/shade/playground/ground_seam/make_grid_texture.d.ts.map +1 -0
  121. package/src/shade/playground/ground_seam/make_grid_texture.js +105 -0
  122. package/src/shade/playground/ground_seam/make_ground.d.ts +70 -0
  123. package/src/shade/playground/ground_seam/make_ground.d.ts.map +1 -0
  124. package/src/shade/playground/ground_seam/make_ground.js +161 -0
  125. package/src/shade/playground/ground_seam/measure_flicker.d.ts +71 -0
  126. package/src/shade/playground/ground_seam/measure_flicker.d.ts.map +1 -0
  127. package/src/shade/playground/ground_seam/measure_flicker.js +531 -0
  128. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  129. package/src/shade/renderer/Renderer.js +2519 -2511
  130. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  131. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +275 -274
  132. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  133. package/src/shade/renderer/light/GPULightCollection.js +323 -314
  134. package/src/shade/renderer/lightmap/GPULightMap.js +336 -336
  135. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  136. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +12 -2
  137. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  138. package/src/shade/renderer/material/standard/fragment_gbuffer.js +3 -1
  139. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  140. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  141. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +3 -1
  142. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  143. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  144. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.js +3 -1
  145. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  146. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.js +3 -1
  147. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts +28 -0
  148. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts.map +1 -0
  149. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.js +39 -0
  150. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts +45 -0
  151. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts.map +1 -1
  152. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.js +94 -61
  153. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts +24 -0
  154. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts.map +1 -0
  155. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.js +43 -0
  156. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +1462 -0
  157. package/src/shade/renderer/texture/TextureManager.d.ts.map +1 -1
  158. package/src/shade/renderer/texture/TextureManager.js +20 -8
  159. package/src/shade/renderer/texture/TextureRole.d.ts +19 -0
  160. package/src/shade/renderer/texture/TextureRole.d.ts.map +1 -0
  161. package/src/shade/renderer/texture/TextureRole.js +26 -0
  162. package/src/shade/renderer/texture/TextureVariantSet.d.ts +71 -0
  163. package/src/shade/renderer/texture/TextureVariantSet.d.ts.map +1 -0
  164. package/src/shade/renderer/texture/TextureVariantSet.js +105 -0
  165. package/src/shade/renderer/texture/download_texture_data.d.ts.map +1 -1
  166. package/src/shade/renderer/texture/download_texture_data.js +22 -0
  167. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts +15 -0
  168. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts.map +1 -0
  169. package/src/shade/renderer/texture/format/gpu_texture_compression_support.js +50 -0
  170. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts +27 -0
  171. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts.map +1 -0
  172. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.js +65 -0
  173. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts +11 -0
  174. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts.map +1 -0
  175. package/src/shade/renderer/texture/format/infer_format_from_parameters.js +88 -0
  176. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts +24 -0
  177. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts.map +1 -0
  178. package/src/shade/renderer/texture/format/texture_format_from_shade_image.js +71 -0
  179. package/src/shade/renderer/texture/format/texture_format_preference.d.ts +22 -0
  180. package/src/shade/renderer/texture/format/texture_format_preference.d.ts.map +1 -0
  181. package/src/shade/renderer/texture/format/texture_format_preference.js +89 -0
  182. package/src/shade/renderer/texture/format/texture_variant_select.d.ts +24 -0
  183. package/src/shade/renderer/texture/format/texture_variant_select.d.ts.map +1 -0
  184. package/src/shade/renderer/texture/format/texture_variant_select.js +38 -0
  185. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts +9 -1
  186. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts.map +1 -1
  187. package/src/shade/renderer/texture/shade_texture_might_have_alpha.js +73 -8
  188. package/src/shade/renderer/texture/source/AlphaMode.d.ts +27 -0
  189. package/src/shade/renderer/texture/source/AlphaMode.d.ts.map +1 -0
  190. package/src/shade/renderer/texture/source/AlphaMode.js +52 -0
  191. package/src/shade/renderer/texture/source/ShadeImage.d.ts +33 -0
  192. package/src/shade/renderer/texture/source/ShadeImage.d.ts.map +1 -1
  193. package/src/shade/renderer/texture/source/ShadeImage.js +74 -0
  194. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts +78 -0
  195. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts.map +1 -0
  196. package/src/shade/renderer/texture/source/ShadeTexelData.js +158 -0
  197. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts +0 -10
  198. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts.map +1 -1
  199. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.js +73 -175
  200. package/src/shade/renderer/texture/texture_write_level.d.ts +32 -0
  201. package/src/shade/renderer/texture/texture_write_level.d.ts.map +1 -0
  202. package/src/shade/renderer/texture/texture_write_level.js +77 -0
  203. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts +8 -1
  204. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts.map +1 -1
  205. package/src/shade/renderer/texture/texture_write_to_gpu.js +62 -37
  206. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts +8 -0
  207. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts.map +1 -1
  208. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +8 -0
  209. package/src/shade/util/texture_mip_extent.d.ts +49 -0
  210. package/src/shade/util/texture_mip_extent.d.ts.map +1 -0
  211. package/src/shade/util/texture_mip_extent.js +87 -0
  212. package/src/avif/native/isobmff/ByteCursor.d.ts.map +0 -1
  213. package/src/engine/asset/loaders/image/codec/Codec.d.ts.map +0 -1
  214. package/src/engine/asset/loaders/image/codec/CodecWithFallback.d.ts.map +0 -1
  215. package/src/shade/util/compute_max_mip_count.d.ts +0 -8
  216. package/src/shade/util/compute_max_mip_count.d.ts.map +0 -1
  217. package/src/shade/util/compute_max_mip_count.js +0 -27
  218. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.d.ts +0 -0
  219. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.js +0 -0
  220. /package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.d.ts +0 -0
@@ -0,0 +1,71 @@
1
+ import { BinaryDataType } from "../../../../core/binary/type/BinaryDataType.js";
2
+ import { isImageBitmap } from "../../../../core/dom/isImageBitmap.js";
3
+ import { ColorSpace } from "../ColorSpace.js";
4
+ import { infer_format_from_parameters } from "./infer_format_from_parameters.js";
5
+
6
+ /**
7
+ * The {@link GPUTextureFormat} a {@link ShadeImage}'s texels are in.
8
+ *
9
+ * A free function rather than a getter on the image, because {@link ShadeImage} describes *data* —
10
+ * a channel count, a data type, a colour space, some bytes — and what WebGPU calls that arrangement
11
+ * is not the data's business. The image stays a model the encoder, the container readers and the
12
+ * asset pipeline can hold without any of them importing a graphics API.
13
+ *
14
+ * What this replaces is the same inference run at *upload* time, inside the descriptor builder. The
15
+ * move matters more than the mechanism: a format worked out by inspecting pixels is a question
16
+ * block-compressed texels cannot answer, so a {@link ShadeTexelData} states its format instead and
17
+ * this function simply reports it. That is what lets both kinds share one path.
18
+ *
19
+ * **Not cached anywhere.** `color_space` is assigned *after* construction in nine places
20
+ * (`gltf_create_material` among them), so a format captured once would leave every glTF albedo map
21
+ * on `rgba8unorm` and silently drop its sRGB decode.
22
+ *
23
+ * @param {ShadeImage} image
24
+ * @returns {GPUTextureFormat}
25
+ * @throws {Error} if the image has no source, or one this does not recognise — a format guessed for
26
+ * texels nobody can describe would put the wrong number of bytes on both sides of the upload
27
+ */
28
+ export function texture_format_from_shade_image(image) {
29
+ const source = image.source;
30
+
31
+ if (source === undefined || source === null) {
32
+ throw new Error(`image ${image.id} has no source, so no format`);
33
+ }
34
+
35
+ if (source.isShadeTexelData === true) {
36
+ // already in a GPU format; it says which
37
+ return source.format;
38
+ }
39
+
40
+ if (source.isSampler2D === true) {
41
+ return infer_format_from_parameters(
42
+ image.channel_count, image.data_type, image.normalized, image.color_space
43
+ );
44
+ }
45
+
46
+ if (isImageBitmap(source)) {
47
+ /*
48
+ A bitmap is whatever the browser decoded it to, which is 8-bit RGBA — the channel count and
49
+ data type the image carries describe the file, not the decode.
50
+ */
51
+ if (image.data_type === BinaryDataType.Float16) {
52
+ return "rgba16float";
53
+ }
54
+
55
+ if (image.color_space === ColorSpace.SRGB) {
56
+ // sampling decodes sRGB to linear for free
57
+ // @see https://www.w3.org/TR/webgpu/#texture-formats
58
+ return "rgba8unorm-srgb";
59
+ }
60
+
61
+ return "rgba8unorm";
62
+ }
63
+
64
+ if (source instanceof ArrayBuffer) {
65
+ return infer_format_from_parameters(
66
+ image.channel_count, image.data_type, image.normalized, image.color_space
67
+ );
68
+ }
69
+
70
+ throw new Error(`Unsupported image data`);
71
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The formats this engine would choose for `role`, best first, filtered to what the device can
3
+ * sample.
4
+ *
5
+ * This is the whole of the engine's part in choosing an encoding. It answers *what would be good*;
6
+ * it does not fetch, does not know what was actually built for a given texture, and does not fall
7
+ * back — all of which belong to the asset layer, which turns an entry into a request and reaches for
8
+ * an uncompressed encoding when none of these can be had.
9
+ *
10
+ * **Names are the non-srgb ones.** `bc7-rgba-unorm` and `bc7-rgba-unorm-srgb` are the same bytes, so
11
+ * srgb-ness is not a property of the encoding to be chosen here — it is picked when the texture is
12
+ * created, from the image's colour space.
13
+ *
14
+ * @param {TextureRole|number} role
15
+ * @param {Set<GPUTextureFormat>} support what this device can sample — see
16
+ * {@link gpu_texture_compression_support}
17
+ * @returns {GPUTextureFormat[]} best first; empty when the device can sample none of them, which is
18
+ * an ordinary answer and means "ask for the uncompressed encoding"
19
+ */
20
+ export function texture_format_preference(role: TextureRole | number, support: Set<GPUTextureFormat>): GPUTextureFormat[];
21
+ import { TextureRole } from "../TextureRole.js";
22
+ //# sourceMappingURL=texture_format_preference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"texture_format_preference.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/format/texture_format_preference.js"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;;GAkBG;AACH,gDANW,WAAW,GAAC,MAAM,WAClB,qBAAqB,GAEnB,kBAAkB,CAU9B;4BAtF2B,mBAAmB"}
@@ -0,0 +1,89 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { TextureFormat } from "../../../descriptor/texture/format/TextureFormat.js";
3
+ import { TextureRole } from "../TextureRole.js";
4
+
5
+ /**
6
+ * Colour with alpha, best first. Albedo and emissive both want this.
7
+ *
8
+ * BC7 is the best quality-per-byte the desktop family offers for four channels; ASTC 4×4 matches its
9
+ * 8 bits per texel on mobile; ETC2's RGBA8 is the last resort for a device with neither.
10
+ *
11
+ * @type {GPUTextureFormat[]}
12
+ */
13
+ const COLOR = [
14
+ TextureFormat.BC7_RGBA_UNORM,
15
+ TextureFormat.ASTC_4X4_UNORM,
16
+ TextureFormat.ETC2_RGBA8UNORM,
17
+ ];
18
+
19
+ /**
20
+ * Tangent-space normals, best first.
21
+ *
22
+ * **Two channels, wherever they can be had.** A tangent-space normal's Z is positive by construction,
23
+ * so {@link chunk_decode_tangent_normal} reconstructs it from XY and the blue channel of a normal map
24
+ * carries nothing. `bc5-rg-unorm` therefore spends on two channels the same 8 bits per texel that BC7
25
+ * spends on four, and `eac-rg11unorm` does the same with 11 bits per channel — both are dramatically
26
+ * better here than a colour codec, which is what makes BC1 or BC7 normals band visibly.
27
+ *
28
+ * ASTC has no two-channel format; its 4×4 block stores XY in RG and the decode ignores the rest.
29
+ *
30
+ * @type {GPUTextureFormat[]}
31
+ */
32
+ const NORMAL = [
33
+ TextureFormat.BC5_RG_UNORM,
34
+ TextureFormat.ASTC_4X4_UNORM,
35
+ TextureFormat.EAC_RG11UNORM,
36
+ ];
37
+
38
+ /**
39
+ * Packed linear data — occlusion, roughness, metalness.
40
+ *
41
+ * The channels are independent quantities rather than a colour, so a codec's cross-channel
42
+ * correlation buys nothing; the reason to prefer BC7 anyway is that it spends its bits per channel
43
+ * rather than on a shared endpoint line, which is what BC1 gets wrong here.
44
+ *
45
+ * @type {GPUTextureFormat[]}
46
+ */
47
+ const ORM = [
48
+ TextureFormat.BC7_RGBA_UNORM,
49
+ TextureFormat.ASTC_4X4_UNORM,
50
+ TextureFormat.ETC2_RGBA8UNORM,
51
+ ];
52
+
53
+ /**
54
+ * @type {Object<TextureRole, GPUTextureFormat[]>}
55
+ */
56
+ const BY_ROLE = {
57
+ [TextureRole.Albedo]: COLOR,
58
+ [TextureRole.Emissive]: COLOR,
59
+ [TextureRole.Normal]: NORMAL,
60
+ [TextureRole.Orm]: ORM,
61
+ };
62
+
63
+ /**
64
+ * The formats this engine would choose for `role`, best first, filtered to what the device can
65
+ * sample.
66
+ *
67
+ * This is the whole of the engine's part in choosing an encoding. It answers *what would be good*;
68
+ * it does not fetch, does not know what was actually built for a given texture, and does not fall
69
+ * back — all of which belong to the asset layer, which turns an entry into a request and reaches for
70
+ * an uncompressed encoding when none of these can be had.
71
+ *
72
+ * **Names are the non-srgb ones.** `bc7-rgba-unorm` and `bc7-rgba-unorm-srgb` are the same bytes, so
73
+ * srgb-ness is not a property of the encoding to be chosen here — it is picked when the texture is
74
+ * created, from the image's colour space.
75
+ *
76
+ * @param {TextureRole|number} role
77
+ * @param {Set<GPUTextureFormat>} support what this device can sample — see
78
+ * {@link gpu_texture_compression_support}
79
+ * @returns {GPUTextureFormat[]} best first; empty when the device can sample none of them, which is
80
+ * an ordinary answer and means "ask for the uncompressed encoding"
81
+ */
82
+ export function texture_format_preference(role, support) {
83
+ assert.enum(role, TextureRole, 'role');
84
+ assert.defined(support, 'support');
85
+
86
+ const preferred = BY_ROLE[role];
87
+
88
+ return preferred.filter(format => support.has(format));
89
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Which encoding of a texture to fetch.
3
+ *
4
+ * The end of the chain {@link gpu_texture_compression_support} and {@link texture_format_preference}
5
+ * begin: what this device can sample, then what the engine would prefer for this kind of content,
6
+ * then — here — the best of those that somebody actually built.
7
+ *
8
+ * **Returns a path and nothing else.** Not the format it chose, because the caller does not need it:
9
+ * a reader determines a payload's format from the payload's own header, which is the only source
10
+ * that cannot be wrong. Handing back a format alongside would invite someone to trust the manifest
11
+ * over the bytes.
12
+ *
13
+ * **Always answers.** A device that supports no compression, or one whose preferred formats were
14
+ * never built, gets {@link TextureVariantSet.fallback} — the same path a tool or a test would ask
15
+ * for. There is no "nothing suitable" case for a caller to handle, because a texture with no
16
+ * universally readable encoding is a broken manifest rather than a runtime condition.
17
+ *
18
+ * @param {TextureVariantSet} variants what was built for this texture
19
+ * @param {TextureRole|number} role what the texture is for
20
+ * @param {Set<GPUTextureFormat>} support what this device can sample
21
+ * @returns {string} the path to fetch
22
+ */
23
+ export function texture_variant_select(variants: TextureVariantSet, role: TextureRole | number, support: Set<GPUTextureFormat>): string;
24
+ //# sourceMappingURL=texture_variant_select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"texture_variant_select.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/format/texture_variant_select.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,0EAJW,cAAY,MAAM,WAClB,qBAAqB,GACnB,MAAM,CAclB"}
@@ -0,0 +1,38 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { texture_format_preference } from "./texture_format_preference.js";
3
+
4
+ /**
5
+ * Which encoding of a texture to fetch.
6
+ *
7
+ * The end of the chain {@link gpu_texture_compression_support} and {@link texture_format_preference}
8
+ * begin: what this device can sample, then what the engine would prefer for this kind of content,
9
+ * then — here — the best of those that somebody actually built.
10
+ *
11
+ * **Returns a path and nothing else.** Not the format it chose, because the caller does not need it:
12
+ * a reader determines a payload's format from the payload's own header, which is the only source
13
+ * that cannot be wrong. Handing back a format alongside would invite someone to trust the manifest
14
+ * over the bytes.
15
+ *
16
+ * **Always answers.** A device that supports no compression, or one whose preferred formats were
17
+ * never built, gets {@link TextureVariantSet.fallback} — the same path a tool or a test would ask
18
+ * for. There is no "nothing suitable" case for a caller to handle, because a texture with no
19
+ * universally readable encoding is a broken manifest rather than a runtime condition.
20
+ *
21
+ * @param {TextureVariantSet} variants what was built for this texture
22
+ * @param {TextureRole|number} role what the texture is for
23
+ * @param {Set<GPUTextureFormat>} support what this device can sample
24
+ * @returns {string} the path to fetch
25
+ */
26
+ export function texture_variant_select(variants, role, support) {
27
+ assert.defined(variants, 'variants');
28
+
29
+ for (const format of texture_format_preference(role, support)) {
30
+ const path = variants.path(format);
31
+
32
+ if (path !== undefined) {
33
+ return path;
34
+ }
35
+ }
36
+
37
+ return variants.fallback;
38
+ }
@@ -1,5 +1,13 @@
1
1
  /**
2
- * If returns false - it means texture definitely has no alpha channel.
2
+ * If this returns false - it means the texture definitely has no alpha channel.
3
+ *
4
+ * Answered from the format, which knows exactly, rather than from the shape of whatever produced it.
5
+ * The previous version guessed from a `Sampler2D`'s `itemSize` and defaulted to `true` for everything
6
+ * else, so a single-channel `ArrayBuffer` image read as possibly-transparent.
7
+ *
8
+ * A false answer is a promise; a true one is only "cannot rule it out". Callers use this to skip
9
+ * alpha handling, so being wrong in the true direction costs a little work and being wrong in the
10
+ * false direction costs correctness.
3
11
  *
4
12
  * @param {ShadeTexture} texture
5
13
  * @returns {boolean}
@@ -1 +1 @@
1
- {"version":3,"file":"shade_texture_might_have_alpha.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/texture/shade_texture_might_have_alpha.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,uEAFa,OAAO,CAqBnB"}
1
+ {"version":3,"file":"shade_texture_might_have_alpha.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/texture/shade_texture_might_have_alpha.js"],"names":[],"mappings":"AA2BA;;;;;;;;;;;;;GAaG;AACH,uEAFa,OAAO,CAmDnB"}
@@ -1,5 +1,40 @@
1
+ import { gpu_texture_format_info } from "../../descriptor/texture/format/gpu_texture_format_info.js";
2
+ import { texture_format_from_shade_image } from "./format/texture_format_from_shade_image.js";
3
+
1
4
  /**
2
- * If returns false - it means texture definitely has no alpha channel.
5
+ * Compressed formats with no alpha channel at all.
6
+ *
7
+ * Everything else in the BC/ETC2/ASTC families either carries alpha or may — including
8
+ * `bc1-rgba-unorm`, which WebGPU exposes only in its RGBA spelling and which decodes one bit of
9
+ * punch-through alpha. Reading BC1 as opaque is the mistake this set exists to not make.
10
+ *
11
+ * @type {Set<GPUTextureFormat>}
12
+ */
13
+ const NO_ALPHA_COMPRESSED = new Set([
14
+ "bc4-r-unorm",
15
+ "bc4-r-snorm",
16
+ "bc5-rg-unorm",
17
+ "bc5-rg-snorm",
18
+ "bc6h-rgb-ufloat",
19
+ "bc6h-rgb-float",
20
+ "etc2-rgb8unorm",
21
+ "etc2-rgb8unorm-srgb",
22
+ "eac-r11unorm",
23
+ "eac-r11snorm",
24
+ "eac-rg11unorm",
25
+ "eac-rg11snorm",
26
+ ]);
27
+
28
+ /**
29
+ * If this returns false - it means the texture definitely has no alpha channel.
30
+ *
31
+ * Answered from the format, which knows exactly, rather than from the shape of whatever produced it.
32
+ * The previous version guessed from a `Sampler2D`'s `itemSize` and defaulted to `true` for everything
33
+ * else, so a single-channel `ArrayBuffer` image read as possibly-transparent.
34
+ *
35
+ * A false answer is a promise; a true one is only "cannot rule it out". Callers use this to skip
36
+ * alpha handling, so being wrong in the true direction costs a little work and being wrong in the
37
+ * false direction costs correctness.
3
38
  *
4
39
  * @param {ShadeTexture} texture
5
40
  * @returns {boolean}
@@ -9,18 +44,48 @@ export function shade_texture_might_have_alpha(texture) {
9
44
  return false;
10
45
  }
11
46
 
12
- const source = texture.image?.source;
47
+ const image = texture.image;
48
+
49
+ if (image === undefined || image === null || image.source === undefined || image.source === null) {
50
+ return false;
51
+ }
52
+
53
+ const source = image.source;
13
54
 
14
- if (source === undefined) {
55
+ if (source.isShadeTexelData !== true && image.channel_count < 4) {
56
+ /*
57
+ Loose pixels with fewer than four channels. The *texture* will have an alpha channel — the
58
+ upload pads RGB to RGBA, because WebGPU has no three-channel format — but it is 1 everywhere
59
+ and the content has no transparency to speak of. Answering from the padded format would call
60
+ every opaque RGB image possibly-transparent.
61
+
62
+ Not asked of a texel payload: its channel count describes nothing, because whatever encoded
63
+ it already chose a format that says how many channels there are.
64
+ */
15
65
  return false;
16
66
  }
17
67
 
18
- if (source.isSampler2D && source.itemSize <= 3) {
68
+ let format;
69
+
70
+ try {
71
+ format = texture_format_from_shade_image(image);
72
+ } catch {
73
+ // a source nothing can name a format for; no basis to promise there is no alpha
74
+ return true;
75
+ }
76
+
77
+ if (NO_ALPHA_COMPRESSED.has(format)) {
19
78
  return false;
20
79
  }
21
80
 
22
- // TODO add support for more image types
81
+ const info = gpu_texture_format_info(format);
82
+
83
+ if (info.block_width !== 1 || info.block_height !== 1) {
84
+ // a compressed format that is not in the set above — BC7, BC1's punch-through, ETC2's
85
+ // RGBA and RGB8A1, every ASTC block size
86
+ return true;
87
+ }
23
88
 
24
- // assume
25
- return true;
26
- }
89
+ // uncompressed: the channel count is in the name, and only a four-channel format has an alpha
90
+ return /^(rgba|bgra)/.test(format);
91
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * What a texture's fourth channel means, and therefore what may be done to it.
3
+ *
4
+ * The renderer wants premultiplied alpha
5
+ * (@see https://www.realtimerendering.com/blog/gpus-prefer-premultiplication/), so an upload may have
6
+ * to multiply RGB by A on the way. Whether that is correct is not a property of the channel existing
7
+ * — it is a property of the channel *meaning opacity*. Scaling a height field, an ORM map or a LUT
8
+ * by its fourth channel corrupts it.
9
+ *
10
+ * Three answers rather than a boolean, because collapsing {@link Opaque } into {@link Straight } does
11
+ * exactly that corruption, and collapsing it into {@link Premultiplied } lies about data that was
12
+ * never multiplied by anything. Plus {@link Unspecified }, which is the *absence* of an answer and
13
+ * belongs in the enum rather than being spelled `undefined` alongside it — a field typed as this
14
+ * enum should only ever hold one of its members.
15
+ *
16
+ * Block-compressed data can only be {@link Premultiplied } or {@link Opaque }: nothing can multiply
17
+ * BC7 blocks without decoding them, so a compressed payload that declares {@link Straight } is a
18
+ * payload whose encoder did not do its job, and the upload says so rather than shipping fringes.
19
+ */
20
+ export type AlphaMode = number;
21
+ export namespace AlphaMode {
22
+ let Unspecified: number;
23
+ let Opaque: number;
24
+ let Straight: number;
25
+ let Premultiplied: number;
26
+ }
27
+ //# sourceMappingURL=AlphaMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlphaMode.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/source/AlphaMode.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;wBAmBU,MAAM"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * What a texture's fourth channel means, and therefore what may be done to it.
3
+ *
4
+ * The renderer wants premultiplied alpha
5
+ * (@see https://www.realtimerendering.com/blog/gpus-prefer-premultiplication/), so an upload may have
6
+ * to multiply RGB by A on the way. Whether that is correct is not a property of the channel existing
7
+ * — it is a property of the channel *meaning opacity*. Scaling a height field, an ORM map or a LUT
8
+ * by its fourth channel corrupts it.
9
+ *
10
+ * Three answers rather than a boolean, because collapsing {@link Opaque} into {@link Straight} does
11
+ * exactly that corruption, and collapsing it into {@link Premultiplied} lies about data that was
12
+ * never multiplied by anything. Plus {@link Unspecified}, which is the *absence* of an answer and
13
+ * belongs in the enum rather than being spelled `undefined` alongside it — a field typed as this
14
+ * enum should only ever hold one of its members.
15
+ *
16
+ * Block-compressed data can only be {@link Premultiplied} or {@link Opaque}: nothing can multiply
17
+ * BC7 blocks without decoding them, so a compressed payload that declares {@link Straight} is a
18
+ * payload whose encoder did not do its job, and the upload says so rather than shipping fringes.
19
+ *
20
+ * @enum {number}
21
+ */
22
+ export const AlphaMode = {
23
+ /**
24
+ * Nothing has said. Derive it — see {@link ShadeImage.alpha_mode}.
25
+ *
26
+ * The state a field starts in, and the one to assign to go back to deriving after an override.
27
+ * A resolved alpha mode is never this: {@link ShadeImage.alpha_mode} answers one of the three
28
+ * below, always.
29
+ */
30
+ Unspecified: 0,
31
+
32
+ /**
33
+ * There is no meaningful alpha. Never premultiply.
34
+ *
35
+ * The state of every non-colour texture: {@link ColorSpace.None} data, single- and two-channel
36
+ * images, anything whose fourth channel carries a value rather than an opacity.
37
+ */
38
+ Opaque: 1,
39
+
40
+ /**
41
+ * Colour with straight (unassociated) alpha — RGB is the colour at full opacity.
42
+ *
43
+ * Premultiplied on upload where that is possible, which for a block format it is not.
44
+ */
45
+ Straight: 2,
46
+
47
+ /**
48
+ * RGB has already been multiplied by A, by an offline encoder or by
49
+ * `copyExternalImageToTexture`. Nothing further to do.
50
+ */
51
+ Premultiplied: 3,
52
+ };
@@ -1,4 +1,9 @@
1
1
  export class ShadeImage {
2
+ /**
3
+ * @param {ShadeTexelData} texel_data
4
+ * @returns {ShadeImage}
5
+ */
6
+ static fromTexelData(texel_data: ShadeTexelData): ShadeImage;
2
7
  /**
3
8
  *
4
9
  * @param {ImageBitmap} bitmap
@@ -58,6 +63,34 @@ export class ShadeImage {
58
63
  get height(): number;
59
64
  set depth(arg: number);
60
65
  get depth(): number;
66
+ set alpha_mode(arg: number);
67
+ /**
68
+ * What this image's fourth channel means — see {@link AlphaMode}.
69
+ *
70
+ * {@link AlphaMode.Unspecified} — the state this starts in — means derive it from
71
+ * {@link color_space}. Derived rather than captured at construction because `color_space` is
72
+ * routinely assigned *after* the image is built: nine places do it, `shade_load_data_image` and
73
+ * `gltf_create_material` among them, and two of them assign {@link ColorSpace.None}. A value
74
+ * frozen at construction would describe the image nobody kept, and would premultiply exactly the
75
+ * data textures the {@link AlphaMode.Opaque} state exists to protect.
76
+ *
77
+ * The derivation is exactly the rule the upload path used to apply inline, so nothing changes
78
+ * for an image that says nothing: non-colour data is never premultiplied, colour is.
79
+ *
80
+ * Assigning {@link AlphaMode.Unspecified} goes back to deriving.
81
+ *
82
+ * @returns {AlphaMode|number} never {@link AlphaMode.Unspecified} — this resolves
83
+ */
84
+ get alpha_mode(): number;
85
+ /**
86
+ * How many mip levels this image supplies.
87
+ *
88
+ * One, for everything that is not a {@link ShadeTexelData} — a bitmap or a sampler is a single
89
+ * level, and any others are generated.
90
+ *
91
+ * @returns {number}
92
+ */
93
+ get level_count(): number;
61
94
  hash(): number;
62
95
  /**
63
96
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ShadeImage.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/source/ShadeImage.js"],"names":[],"mappings":"AASA;IAoHI;;;;OAIG;IACH,+BAHW,WAAW,GACV,UAAU,CAkBrB;IAED;;;;OAIG;IACH,0CAFY,UAAU,CAqBrB;IAED;;;;;;;;;OASG;IACH,qCARW,WAAW,iBACX,MAAM,aACN,cAAc,UACd,MAAM,WACN,MAAM,UACN,MAAM,GACL,UAAU,CAkCrB;IAxMD,iBAEC;IAED;;;OAGG;IACH,aAFU,UAAU,GAAC,MAAM,CAES;IAgBpC,2BAEC;IAVD;;;OAGG;IACH,wBAEC;IAoBD,+BAEC;IAVD;;;OAGG;IACH,4BAEC;IAMD;;;;OAIG;IACH,YAFU,OAAO,CAEC;IAelB,qBAEC;IAVD;;;OAGG;IACH,kBAEC;IAYD,uBAEC;IAND,oBAEC;IAUD,wBAEC;IAND,qBAEC;IAUD,uBAEC;IAND,oBAEC;IAMD,eAEC;IAED;;;;OAIG;IACH,cAHW,UAAU,GACR,OAAO,CAInB;IAgGL;;;OAGG;IACH,uBAFU,OAAO,CAEgB;;CANhC;2BApN0B,kBAAkB;+BAJd,gDAAgD"}
1
+ {"version":3,"file":"ShadeImage.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/texture/source/ShadeImage.js"],"names":[],"mappings":"AAUA;IA6JI;;;OAGG;IACH,kDAFa,UAAU,CAetB;IAeD;;;;OAIG;IACH,+BAHW,WAAW,GACV,UAAU,CAkBrB;IAED;;;;OAIG;IACH,0CAFY,UAAU,CAqBrB;IAED;;;;;;;;;OASG;IACH,qCARW,WAAW,iBACX,MAAM,aACN,cAAc,UACd,MAAM,WACN,MAAM,UACN,MAAM,GACL,UAAU,CAkCrB;IAjRD,iBAEC;IAED;;;OAGG;IACH,aAFU,UAAU,GAAC,MAAM,CAES;IAgBpC,2BAEC;IAVD;;;OAGG;IACH,wBAEC;IAoBD,+BAEC;IAVD;;;OAGG;IACH,4BAEC;IAMD;;;;OAIG;IACH,YAFU,OAAO,CAEC;IAelB,qBAEC;IAVD;;;OAGG;IACH,kBAEC;IAYD,uBAEC;IAND,oBAEC;IAUD,wBAEC;IAND,qBAEC;IAUD,uBAEC;IAND,oBAEC;IAoCD,4BAIC;IA7BD;;;;;;;;;;;;;;;;OAgBG;IACH,yBAMC;IAQD;;;;;;;OAOG;IACH,0BAQC;IAqBD,eAEC;IAED;;;;OAIG;IACH,cAHW,UAAU,GACR,OAAO,CAInB;IAgGL;;;OAGG;IACH,uBAFU,OAAO,CAEgB;;CANhC;2BA9R0B,kBAAkB;+BAJd,gDAAgD"}
@@ -4,6 +4,7 @@ import {
4
4
  compute_binary_data_type_from_typed_array
5
5
  } from "../../../../core/collection/array/typed/compute_binary_data_type_from_typed_array.js";
6
6
  import { ColorSpace } from "../ColorSpace.js";
7
+ import { AlphaMode } from "./AlphaMode.js";
7
8
 
8
9
  let id_counter = 0;
9
10
 
@@ -110,6 +111,79 @@ export class ShadeImage {
110
111
  this.#size[2] = value;
111
112
  }
112
113
 
114
+ /**
115
+ * @type {AlphaMode|number}
116
+ */
117
+ #alpha_mode = AlphaMode.Unspecified;
118
+
119
+ /**
120
+ * What this image's fourth channel means — see {@link AlphaMode}.
121
+ *
122
+ * {@link AlphaMode.Unspecified} — the state this starts in — means derive it from
123
+ * {@link color_space}. Derived rather than captured at construction because `color_space` is
124
+ * routinely assigned *after* the image is built: nine places do it, `shade_load_data_image` and
125
+ * `gltf_create_material` among them, and two of them assign {@link ColorSpace.None}. A value
126
+ * frozen at construction would describe the image nobody kept, and would premultiply exactly the
127
+ * data textures the {@link AlphaMode.Opaque} state exists to protect.
128
+ *
129
+ * The derivation is exactly the rule the upload path used to apply inline, so nothing changes
130
+ * for an image that says nothing: non-colour data is never premultiplied, colour is.
131
+ *
132
+ * Assigning {@link AlphaMode.Unspecified} goes back to deriving.
133
+ *
134
+ * @returns {AlphaMode|number} never {@link AlphaMode.Unspecified} — this resolves
135
+ */
136
+ get alpha_mode() {
137
+ if (this.#alpha_mode !== AlphaMode.Unspecified) {
138
+ return this.#alpha_mode;
139
+ }
140
+
141
+ return this.color_space === ColorSpace.None ? AlphaMode.Opaque : AlphaMode.Straight;
142
+ }
143
+
144
+ set alpha_mode(value) {
145
+ assert.enum(value, AlphaMode, 'alpha_mode');
146
+
147
+ this.#alpha_mode = value;
148
+ }
149
+
150
+ /**
151
+ * How many mip levels this image supplies.
152
+ *
153
+ * One, for everything that is not a {@link ShadeTexelData} — a bitmap or a sampler is a single
154
+ * level, and any others are generated.
155
+ *
156
+ * @returns {number}
157
+ */
158
+ get level_count() {
159
+ const source = this.#source;
160
+
161
+ if (source !== undefined && source !== null && source.isShadeTexelData === true) {
162
+ return source.level_count;
163
+ }
164
+
165
+ return 1;
166
+ }
167
+
168
+ /**
169
+ * @param {ShadeTexelData} texel_data
170
+ * @returns {ShadeImage}
171
+ */
172
+ static fromTexelData(texel_data) {
173
+ assert.defined(texel_data, 'texel_data');
174
+ assert.equal(texel_data.isShadeTexelData, true, 'texel_data.isShadeTexelData !== true');
175
+
176
+ const r = new ShadeImage();
177
+
178
+ r.#source = texel_data;
179
+
180
+ r.#size[0] = texel_data.width;
181
+ r.#size[1] = texel_data.height;
182
+ r.#size[2] = texel_data.depth;
183
+
184
+ return r;
185
+ }
186
+
113
187
  hash() {
114
188
  return this.#id;
115
189
  }