@woosh/meep-engine 3.9.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (832) hide show
  1. package/README.md +168 -152
  2. package/build/bundle-worker-image-decoder.js +1 -1
  3. package/build/bundle-worker-terrain.js +1 -1
  4. package/package.json +3 -2
  5. package/src/REVIEW_2026_08_06.md +610 -610
  6. package/src/avif/encode_image_source.d.ts +70 -0
  7. package/src/avif/encode_image_source.d.ts.map +1 -0
  8. package/src/avif/encode_image_source.js +117 -0
  9. package/src/avif/encoder_worker.js +15 -126
  10. package/src/avif/index.d.ts +4 -2
  11. package/src/avif/index.js +21 -2
  12. package/src/avif/native/DECISIONS.md +537 -0
  13. package/src/avif/native/NOTICE.md +61 -0
  14. package/src/avif/native/api/AvifDecoder.d.ts +67 -0
  15. package/src/avif/native/api/AvifDecoder.d.ts.map +1 -0
  16. package/src/avif/native/api/AvifDecoder.js +121 -0
  17. package/src/avif/native/api/apply_transformations.d.ts +30 -0
  18. package/src/avif/native/api/apply_transformations.d.ts.map +1 -0
  19. package/src/avif/native/api/apply_transformations.js +120 -0
  20. package/src/avif/native/api/avif_to_sampler2d.d.ts +25 -0
  21. package/src/avif/native/api/avif_to_sampler2d.d.ts.map +1 -0
  22. package/src/avif/native/api/avif_to_sampler2d.js +42 -0
  23. package/src/avif/native/api/convert_to_rgba.d.ts +54 -0
  24. package/src/avif/native/api/convert_to_rgba.d.ts.map +1 -0
  25. package/src/avif/native/api/convert_to_rgba.js +204 -0
  26. package/src/avif/native/api/decode_avif.d.ts +24 -0
  27. package/src/avif/native/api/decode_avif.d.ts.map +1 -0
  28. package/src/avif/native/api/decode_avif.js +111 -0
  29. package/src/avif/native/api/decode_image_item.d.ts +18 -0
  30. package/src/avif/native/api/decode_image_item.d.ts.map +1 -0
  31. package/src/avif/native/api/decode_image_item.js +234 -0
  32. package/src/avif/native/api/encode_avif.d.ts +73 -0
  33. package/src/avif/native/api/encode_avif.d.ts.map +1 -0
  34. package/src/avif/native/api/encode_avif.js +488 -0
  35. package/src/avif/native/api/sampler2d_to_avif.d.ts +22 -0
  36. package/src/avif/native/api/sampler2d_to_avif.d.ts.map +1 -0
  37. package/src/avif/native/api/sampler2d_to_avif.js +108 -0
  38. package/src/avif/native/av1/decode/Av1FrameContext.d.ts +286 -0
  39. package/src/avif/native/av1/decode/Av1FrameContext.d.ts.map +1 -0
  40. package/src/avif/native/av1/decode/Av1FrameContext.js +622 -0
  41. package/src/avif/native/av1/decode/block_decoded.d.ts +27 -0
  42. package/src/avif/native/av1/decode/block_decoded.d.ts.map +1 -0
  43. package/src/avif/native/av1/decode/block_decoded.js +59 -0
  44. package/src/avif/native/av1/decode/decode_av1_still.d.ts +22 -0
  45. package/src/avif/native/av1/decode/decode_av1_still.d.ts.map +1 -0
  46. package/src/avif/native/av1/decode/decode_av1_still.js +309 -0
  47. package/src/avif/native/av1/decode/decode_coefficients.d.ts +20 -0
  48. package/src/avif/native/av1/decode/decode_coefficients.d.ts.map +1 -0
  49. package/src/avif/native/av1/decode/decode_coefficients.js +259 -0
  50. package/src/avif/native/av1/decode/decode_palette.d.ts +28 -0
  51. package/src/avif/native/av1/decode/decode_palette.d.ts.map +1 -0
  52. package/src/avif/native/av1/decode/decode_palette.js +442 -0
  53. package/src/avif/native/av1/decode/decode_tile.d.ts +14 -0
  54. package/src/avif/native/av1/decode/decode_tile.d.ts.map +1 -0
  55. package/src/avif/native/av1/decode/decode_tile.js +1213 -0
  56. package/src/avif/native/av1/decode/get_scan.d.ts +16 -0
  57. package/src/avif/native/av1/decode/get_scan.d.ts.map +1 -0
  58. package/src/avif/native/av1/decode/get_scan.js +138 -0
  59. package/src/avif/native/av1/decode/read_lr.d.ts +35 -0
  60. package/src/avif/native/av1/decode/read_lr.d.ts.map +1 -0
  61. package/src/avif/native/av1/decode/read_lr.js +277 -0
  62. package/src/avif/native/av1/decode/read_transform_type.d.ts +19 -0
  63. package/src/avif/native/av1/decode/read_transform_type.d.ts.map +1 -0
  64. package/src/avif/native/av1/decode/read_transform_type.js +92 -0
  65. package/src/avif/native/av1/decode/reconstruct.d.ts +36 -0
  66. package/src/avif/native/av1/decode/reconstruct.d.ts.map +1 -0
  67. package/src/avif/native/av1/decode/reconstruct.js +241 -0
  68. package/src/avif/native/av1/decode/transform_type.d.ts +49 -0
  69. package/src/avif/native/av1/decode/transform_type.d.ts.map +1 -0
  70. package/src/avif/native/av1/decode/transform_type.js +137 -0
  71. package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts +81 -0
  72. package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts.map +1 -0
  73. package/src/avif/native/av1/encode/Av1BlockSnapshot.js +299 -0
  74. package/src/avif/native/av1/encode/Av1EncodeContext.d.ts +384 -0
  75. package/src/avif/native/av1/encode/Av1EncodeContext.d.ts.map +1 -0
  76. package/src/avif/native/av1/encode/Av1EncodeContext.js +590 -0
  77. package/src/avif/native/av1/encode/build_headers.d.ts +50 -0
  78. package/src/avif/native/av1/encode/build_headers.d.ts.map +1 -0
  79. package/src/avif/native/av1/encode/build_headers.js +359 -0
  80. package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts +26 -0
  81. package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts.map +1 -0
  82. package/src/avif/native/av1/encode/choose_chroma_alpha.js +90 -0
  83. package/src/avif/native/av1/encode/choose_filters.d.ts +21 -0
  84. package/src/avif/native/av1/encode/choose_filters.d.ts.map +1 -0
  85. package/src/avif/native/av1/encode/choose_filters.js +318 -0
  86. package/src/avif/native/av1/encode/choose_restoration.d.ts +35 -0
  87. package/src/avif/native/av1/encode/choose_restoration.d.ts.map +1 -0
  88. package/src/avif/native/av1/encode/choose_restoration.js +616 -0
  89. package/src/avif/native/av1/encode/decode_effort.d.ts +57 -0
  90. package/src/avif/native/av1/encode/decode_effort.d.ts.map +1 -0
  91. package/src/avif/native/av1/encode/decode_effort.js +59 -0
  92. package/src/avif/native/av1/encode/encode_av1_still.d.ts +17 -0
  93. package/src/avif/native/av1/encode/encode_av1_still.d.ts.map +1 -0
  94. package/src/avif/native/av1/encode/encode_av1_still.js +194 -0
  95. package/src/avif/native/av1/encode/encode_effort.d.ts +56 -0
  96. package/src/avif/native/av1/encode/encode_effort.d.ts.map +1 -0
  97. package/src/avif/native/av1/encode/encode_effort.js +97 -0
  98. package/src/avif/native/av1/encode/encode_tile.d.ts +26 -0
  99. package/src/avif/native/av1/encode/encode_tile.d.ts.map +1 -0
  100. package/src/avif/native/av1/encode/encode_tile.js +2331 -0
  101. package/src/avif/native/av1/encode/quantise.d.ts +51 -0
  102. package/src/avif/native/av1/encode/quantise.d.ts.map +1 -0
  103. package/src/avif/native/av1/encode/quantise.js +212 -0
  104. package/src/avif/native/av1/encode/write_coefficients.d.ts +42 -0
  105. package/src/avif/native/av1/encode/write_coefficients.d.ts.map +1 -0
  106. package/src/avif/native/av1/encode/write_coefficients.js +367 -0
  107. package/src/avif/native/av1/encode/write_lr.d.ts +33 -0
  108. package/src/avif/native/av1/encode/write_lr.d.ts.map +1 -0
  109. package/src/avif/native/av1/encode/write_lr.js +174 -0
  110. package/src/avif/native/av1/encode/write_transform_type.d.ts +21 -0
  111. package/src/avif/native/av1/encode/write_transform_type.d.ts.map +1 -0
  112. package/src/avif/native/av1/encode/write_transform_type.js +70 -0
  113. package/src/avif/native/av1/entropy/CdfContext.d.ts +167 -0
  114. package/src/avif/native/av1/entropy/CdfContext.d.ts.map +1 -0
  115. package/src/avif/native/av1/entropy/CdfContext.js +433 -0
  116. package/src/avif/native/av1/entropy/SymbolReader.d.ts +103 -0
  117. package/src/avif/native/av1/entropy/SymbolReader.d.ts.map +1 -0
  118. package/src/avif/native/av1/entropy/SymbolReader.js +317 -0
  119. package/src/avif/native/av1/entropy/SymbolWriter.d.ts +107 -0
  120. package/src/avif/native/av1/entropy/SymbolWriter.d.ts.map +1 -0
  121. package/src/avif/native/av1/entropy/SymbolWriter.js +352 -0
  122. package/src/avif/native/av1/entropy/coefficient_context.d.ts +71 -0
  123. package/src/avif/native/av1/entropy/coefficient_context.d.ts.map +1 -0
  124. package/src/avif/native/av1/entropy/coefficient_context.js +325 -0
  125. package/src/avif/native/av1/entropy/partition_cdf.d.ts +54 -0
  126. package/src/avif/native/av1/entropy/partition_cdf.d.ts.map +1 -0
  127. package/src/avif/native/av1/entropy/partition_cdf.js +98 -0
  128. package/src/avif/native/av1/entropy/symbol_cost.d.ts +37 -0
  129. package/src/avif/native/av1/entropy/symbol_cost.d.ts.map +1 -0
  130. package/src/avif/native/av1/entropy/symbol_cost.js +76 -0
  131. package/src/avif/native/av1/filter/cdef.d.ts +15 -0
  132. package/src/avif/native/av1/filter/cdef.d.ts.map +1 -0
  133. package/src/avif/native/av1/filter/cdef.js +313 -0
  134. package/src/avif/native/av1/filter/loop_filter.d.ts +14 -0
  135. package/src/avif/native/av1/filter/loop_filter.d.ts.map +1 -0
  136. package/src/avif/native/av1/filter/loop_filter.js +377 -0
  137. package/src/avif/native/av1/filter/loop_restoration.d.ts +42 -0
  138. package/src/avif/native/av1/filter/loop_restoration.d.ts.map +1 -0
  139. package/src/avif/native/av1/filter/loop_restoration.js +451 -0
  140. package/src/avif/native/av1/filter/superres.d.ts +18 -0
  141. package/src/avif/native/av1/filter/superres.d.ts.map +1 -0
  142. package/src/avif/native/av1/filter/superres.js +86 -0
  143. package/src/avif/native/av1/grain/FilmGrainState.d.ts +62 -0
  144. package/src/avif/native/av1/grain/FilmGrainState.d.ts.map +1 -0
  145. package/src/avif/native/av1/grain/FilmGrainState.js +87 -0
  146. package/src/avif/native/av1/grain/film_grain.d.ts +16 -0
  147. package/src/avif/native/av1/grain/film_grain.d.ts.map +1 -0
  148. package/src/avif/native/av1/grain/film_grain.js +623 -0
  149. package/src/avif/native/av1/obu/FrameHeader.d.ts +248 -0
  150. package/src/avif/native/av1/obu/FrameHeader.d.ts.map +1 -0
  151. package/src/avif/native/av1/obu/FrameHeader.js +381 -0
  152. package/src/avif/native/av1/obu/ObuHeader.d.ts +53 -0
  153. package/src/avif/native/av1/obu/ObuHeader.d.ts.map +1 -0
  154. package/src/avif/native/av1/obu/ObuHeader.js +62 -0
  155. package/src/avif/native/av1/obu/SequenceHeader.d.ts +157 -0
  156. package/src/avif/native/av1/obu/SequenceHeader.d.ts.map +1 -0
  157. package/src/avif/native/av1/obu/SequenceHeader.js +225 -0
  158. package/src/avif/native/av1/obu/for_each_obu.d.ts +24 -0
  159. package/src/avif/native/av1/obu/for_each_obu.d.ts.map +1 -0
  160. package/src/avif/native/av1/obu/for_each_obu.js +50 -0
  161. package/src/avif/native/av1/obu/parse_frame_header.d.ts +18 -0
  162. package/src/avif/native/av1/obu/parse_frame_header.d.ts.map +1 -0
  163. package/src/avif/native/av1/obu/parse_frame_header.js +841 -0
  164. package/src/avif/native/av1/obu/parse_obu_header.d.ts +23 -0
  165. package/src/avif/native/av1/obu/parse_obu_header.d.ts.map +1 -0
  166. package/src/avif/native/av1/obu/parse_obu_header.js +69 -0
  167. package/src/avif/native/av1/obu/parse_sequence_header.d.ts +17 -0
  168. package/src/avif/native/av1/obu/parse_sequence_header.d.ts.map +1 -0
  169. package/src/avif/native/av1/obu/parse_sequence_header.js +263 -0
  170. package/src/avif/native/av1/obu/write_frame_header.d.ts +16 -0
  171. package/src/avif/native/av1/obu/write_frame_header.d.ts.map +1 -0
  172. package/src/avif/native/av1/obu/write_frame_header.js +579 -0
  173. package/src/avif/native/av1/obu/write_sequence_header.d.ts +17 -0
  174. package/src/avif/native/av1/obu/write_sequence_header.d.ts.map +1 -0
  175. package/src/avif/native/av1/obu/write_sequence_header.js +189 -0
  176. package/src/avif/native/av1/predict/IntraPredictionState.d.ts +49 -0
  177. package/src/avif/native/av1/predict/IntraPredictionState.d.ts.map +1 -0
  178. package/src/avif/native/av1/predict/IntraPredictionState.js +58 -0
  179. package/src/avif/native/av1/predict/intra_filter_type.d.ts +17 -0
  180. package/src/avif/native/av1/predict/intra_filter_type.d.ts.map +1 -0
  181. package/src/avif/native/av1/predict/intra_filter_type.js +79 -0
  182. package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts +50 -0
  183. package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts.map +1 -0
  184. package/src/avif/native/av1/predict/predict_chroma_from_luma.js +153 -0
  185. package/src/avif/native/av1/predict/predict_intra.d.ts +42 -0
  186. package/src/avif/native/av1/predict/predict_intra.d.ts.map +1 -0
  187. package/src/avif/native/av1/predict/predict_intra.js +803 -0
  188. package/src/avif/native/av1/predict/predict_palette.d.ts +23 -0
  189. package/src/avif/native/av1/predict/predict_palette.d.ts.map +1 -0
  190. package/src/avif/native/av1/predict/predict_palette.js +36 -0
  191. package/src/avif/native/av1/tables/av1_symbols.d.ts +411 -0
  192. package/src/avif/native/av1/tables/av1_symbols.d.ts.map +1 -0
  193. package/src/avif/native/av1/tables/av1_symbols.js +419 -0
  194. package/src/avif/native/av1/tables/block_tables.d.ts +181 -0
  195. package/src/avif/native/av1/tables/block_tables.d.ts.map +1 -0
  196. package/src/avif/native/av1/tables/block_tables.js +270 -0
  197. package/src/avif/native/av1/tables/cdf_tables.d.ts +961 -0
  198. package/src/avif/native/av1/tables/cdf_tables.d.ts.map +1 -0
  199. package/src/avif/native/av1/tables/cdf_tables.js +1893 -0
  200. package/src/avif/native/av1/tables/coefficient_tables.d.ts +41 -0
  201. package/src/avif/native/av1/tables/coefficient_tables.d.ts.map +1 -0
  202. package/src/avif/native/av1/tables/coefficient_tables.js +82 -0
  203. package/src/avif/native/av1/tables/derived_block_tables.d.ts +15 -0
  204. package/src/avif/native/av1/tables/derived_block_tables.d.ts.map +1 -0
  205. package/src/avif/native/av1/tables/derived_block_tables.js +23 -0
  206. package/src/avif/native/av1/tables/filter_tables.d.ts +171 -0
  207. package/src/avif/native/av1/tables/filter_tables.d.ts.map +1 -0
  208. package/src/avif/native/av1/tables/filter_tables.js +313 -0
  209. package/src/avif/native/av1/tables/grain_tables.d.ts +11 -0
  210. package/src/avif/native/av1/tables/grain_tables.d.ts.map +1 -0
  211. package/src/avif/native/av1/tables/grain_tables.js +190 -0
  212. package/src/avif/native/av1/tables/prediction_tables.d.ts +141 -0
  213. package/src/avif/native/av1/tables/prediction_tables.d.ts.map +1 -0
  214. package/src/avif/native/av1/tables/prediction_tables.js +223 -0
  215. package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts +21 -0
  216. package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts.map +1 -0
  217. package/src/avif/native/av1/tables/quantizer_matrix_tables.js +38 -0
  218. package/src/avif/native/av1/tables/quantizer_tables.d.ts +21 -0
  219. package/src/avif/native/av1/tables/quantizer_tables.d.ts.map +1 -0
  220. package/src/avif/native/av1/tables/quantizer_tables.js +160 -0
  221. package/src/avif/native/av1/tables/scan_tables.d.ts +321 -0
  222. package/src/avif/native/av1/tables/scan_tables.d.ts.map +1 -0
  223. package/src/avif/native/av1/tables/scan_tables.js +727 -0
  224. package/src/avif/native/av1/tables/segmentation_tables.d.ts +31 -0
  225. package/src/avif/native/av1/tables/segmentation_tables.d.ts.map +1 -0
  226. package/src/avif/native/av1/tables/segmentation_tables.js +48 -0
  227. package/src/avif/native/av1/tables/transform_tables.d.ts +91 -0
  228. package/src/avif/native/av1/tables/transform_tables.d.ts.map +1 -0
  229. package/src/avif/native/av1/tables/transform_tables.js +140 -0
  230. package/src/avif/native/av1/tables/unpack_table.d.ts +33 -0
  231. package/src/avif/native/av1/tables/unpack_table.d.ts.map +1 -0
  232. package/src/avif/native/av1/tables/unpack_table.js +73 -0
  233. package/src/avif/native/av1/transform/forward_transform_2d.d.ts +61 -0
  234. package/src/avif/native/av1/transform/forward_transform_2d.d.ts.map +1 -0
  235. package/src/avif/native/av1/transform/forward_transform_2d.js +574 -0
  236. package/src/avif/native/av1/transform/inverse_transform_1d.d.ts +76 -0
  237. package/src/avif/native/av1/transform/inverse_transform_1d.d.ts.map +1 -0
  238. package/src/avif/native/av1/transform/inverse_transform_1d.js +590 -0
  239. package/src/avif/native/av1/transform/inverse_transform_2d.d.ts +46 -0
  240. package/src/avif/native/av1/transform/inverse_transform_2d.d.ts.map +1 -0
  241. package/src/avif/native/av1/transform/inverse_transform_2d.js +180 -0
  242. package/src/avif/native/av1/util/ceil_log2.d.ts +13 -0
  243. package/src/avif/native/av1/util/ceil_log2.d.ts.map +1 -0
  244. package/src/avif/native/av1/util/ceil_log2.js +18 -0
  245. package/src/avif/native/av1/util/floor_log2.d.ts +20 -0
  246. package/src/avif/native/av1/util/floor_log2.d.ts.map +1 -0
  247. package/src/avif/native/av1/util/floor_log2.js +20 -0
  248. package/src/avif/native/av1/util/round2.d.ts +22 -0
  249. package/src/avif/native/av1/util/round2.d.ts.map +1 -0
  250. package/src/avif/native/av1/util/round2.js +27 -0
  251. package/src/avif/native/av1/util/round2_signed.d.ts +14 -0
  252. package/src/avif/native/av1/util/round2_signed.d.ts.map +1 -0
  253. package/src/avif/native/av1/util/round2_signed.js +17 -0
  254. package/src/avif/native/av1/util/tile_log2.d.ts +16 -0
  255. package/src/avif/native/av1/util/tile_log2.d.ts.map +1 -0
  256. package/src/avif/native/av1/util/tile_log2.js +23 -0
  257. package/src/avif/native/bitstream/BitReader.d.ts +174 -0
  258. package/src/avif/native/bitstream/BitReader.d.ts.map +1 -0
  259. package/src/avif/native/bitstream/BitReader.js +391 -0
  260. package/src/avif/native/bitstream/BitWriter.d.ts +122 -0
  261. package/src/avif/native/bitstream/BitWriter.d.ts.map +1 -0
  262. package/src/avif/native/bitstream/BitWriter.js +351 -0
  263. package/src/avif/native/color/ColourTransform.d.ts +62 -0
  264. package/src/avif/native/color/ColourTransform.d.ts.map +1 -0
  265. package/src/avif/native/color/ColourTransform.js +189 -0
  266. package/src/avif/native/color/YuvImage.d.ts +51 -0
  267. package/src/avif/native/color/YuvImage.d.ts.map +1 -0
  268. package/src/avif/native/color/YuvImage.js +66 -0
  269. package/src/avif/native/color/clamp_sample.d.ts +18 -0
  270. package/src/avif/native/color/clamp_sample.d.ts.map +1 -0
  271. package/src/avif/native/color/clamp_sample.js +25 -0
  272. package/src/avif/native/color/linear_to_transfer.d.ts +24 -0
  273. package/src/avif/native/color/linear_to_transfer.d.ts.map +1 -0
  274. package/src/avif/native/color/linear_to_transfer.js +126 -0
  275. package/src/avif/native/color/primaries.d.ts +20 -0
  276. package/src/avif/native/color/primaries.d.ts.map +1 -0
  277. package/src/avif/native/color/primaries.js +94 -0
  278. package/src/avif/native/color/rgb_row_to_ycbcr.d.ts +20 -0
  279. package/src/avif/native/color/rgb_row_to_ycbcr.d.ts.map +1 -0
  280. package/src/avif/native/color/rgb_row_to_ycbcr.js +78 -0
  281. package/src/avif/native/color/transfer_to_linear.d.ts +49 -0
  282. package/src/avif/native/color/transfer_to_linear.d.ts.map +1 -0
  283. package/src/avif/native/color/transfer_to_linear.js +171 -0
  284. package/src/avif/native/color/upsample_chroma_row.d.ts +35 -0
  285. package/src/avif/native/color/upsample_chroma_row.d.ts.map +1 -0
  286. package/src/avif/native/color/upsample_chroma_row.js +89 -0
  287. package/src/avif/native/color/ycbcr_row_to_rgb.d.ts +23 -0
  288. package/src/avif/native/color/ycbcr_row_to_rgb.d.ts.map +1 -0
  289. package/src/avif/native/color/ycbcr_row_to_rgb.js +78 -0
  290. package/src/avif/native/heif/AvifFile.d.ts +112 -0
  291. package/src/avif/native/heif/AvifFile.d.ts.map +1 -0
  292. package/src/avif/native/heif/AvifFile.js +142 -0
  293. package/src/avif/native/heif/ItemProperty.d.ts +244 -0
  294. package/src/avif/native/heif/ItemProperty.d.ts.map +1 -0
  295. package/src/avif/native/heif/ItemProperty.js +321 -0
  296. package/src/avif/native/heif/find_item_property.d.ts +17 -0
  297. package/src/avif/native/heif/find_item_property.d.ts.map +1 -0
  298. package/src/avif/native/heif/find_item_property.js +28 -0
  299. package/src/avif/native/heif/find_item_references.d.ts +16 -0
  300. package/src/avif/native/heif/find_item_references.d.ts.map +1 -0
  301. package/src/avif/native/heif/find_item_references.js +29 -0
  302. package/src/avif/native/heif/find_items_referencing.d.ts +17 -0
  303. package/src/avif/native/heif/find_items_referencing.d.ts.map +1 -0
  304. package/src/avif/native/heif/find_items_referencing.js +36 -0
  305. package/src/avif/native/heif/parse_avif_file.d.ts +15 -0
  306. package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -0
  307. package/src/avif/native/heif/parse_avif_file.js +383 -0
  308. package/src/avif/native/heif/parse_image_grid.d.ts +18 -0
  309. package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -0
  310. package/src/avif/native/heif/parse_image_grid.js +48 -0
  311. package/src/avif/native/heif/parse_item_property.d.ts +18 -0
  312. package/src/avif/native/heif/parse_item_property.d.ts.map +1 -0
  313. package/src/avif/native/heif/parse_item_property.js +243 -0
  314. package/src/avif/native/heif/read_item_data.d.ts +22 -0
  315. package/src/avif/native/heif/read_item_data.d.ts.map +1 -0
  316. package/src/avif/native/heif/read_item_data.js +97 -0
  317. package/src/avif/native/heif/write_avif_file.d.ts +50 -0
  318. package/src/avif/native/heif/write_avif_file.d.ts.map +1 -0
  319. package/src/avif/native/heif/write_avif_file.js +348 -0
  320. package/src/avif/native/heif/write_item_property.d.ts +16 -0
  321. package/src/avif/native/heif/write_item_property.d.ts.map +1 -0
  322. package/src/avif/native/heif/write_item_property.js +182 -0
  323. package/src/avif/native/index.d.ts +14 -0
  324. package/src/avif/native/index.d.ts.map +1 -0
  325. package/src/avif/native/index.js +56 -0
  326. package/src/avif/native/isobmff/BoxHeader.d.ts +50 -0
  327. package/src/avif/native/isobmff/BoxHeader.d.ts.map +1 -0
  328. package/src/avif/native/isobmff/BoxHeader.js +57 -0
  329. package/src/avif/native/isobmff/BoxWriter.d.ts +118 -0
  330. package/src/avif/native/isobmff/BoxWriter.d.ts.map +1 -0
  331. package/src/avif/native/isobmff/BoxWriter.js +264 -0
  332. package/src/avif/native/isobmff/ByteCursor.d.ts +125 -0
  333. package/src/avif/native/isobmff/ByteCursor.d.ts.map +1 -0
  334. package/src/avif/native/isobmff/ByteCursor.js +298 -0
  335. package/src/avif/native/isobmff/for_each_box.d.ts +23 -0
  336. package/src/avif/native/isobmff/for_each_box.d.ts.map +1 -0
  337. package/src/avif/native/isobmff/for_each_box.js +39 -0
  338. package/src/avif/native/isobmff/read_box_header.d.ts +22 -0
  339. package/src/avif/native/isobmff/read_box_header.d.ts.map +1 -0
  340. package/src/avif/native/isobmff/read_box_header.js +59 -0
  341. package/src/avif/native/isobmff/read_full_box_header.d.ts +26 -0
  342. package/src/avif/native/isobmff/read_full_box_header.d.ts.map +1 -0
  343. package/src/avif/native/isobmff/read_full_box_header.js +33 -0
  344. package/src/avif/threaded_image_encoder.d.ts +7 -2
  345. package/src/avif/threaded_image_encoder.d.ts.map +1 -1
  346. package/src/avif/threaded_image_encoder.js +107 -21
  347. package/src/core/binary/BinaryBuffer.d.ts +40 -0
  348. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  349. package/src/core/binary/BinaryBuffer.js +100 -0
  350. package/src/core/binary/hash/crc32.d.ts +16 -0
  351. package/src/core/binary/hash/crc32.d.ts.map +1 -0
  352. package/src/core/binary/hash/crc32.js +96 -0
  353. package/src/core/binary/utf8/utf8_decode.d.ts +21 -0
  354. package/src/core/binary/utf8/utf8_decode.d.ts.map +1 -0
  355. package/src/core/binary/utf8/utf8_decode.js +43 -0
  356. package/src/core/binary/utf8/utf8_encode_into.d.ts +20 -0
  357. package/src/core/binary/utf8/utf8_encode_into.d.ts.map +1 -0
  358. package/src/core/binary/utf8/utf8_encode_into.js +48 -0
  359. package/src/core/binary/utf8/utf8_encoded_length.d.ts +14 -0
  360. package/src/core/binary/utf8/utf8_encoded_length.d.ts.map +1 -0
  361. package/src/core/binary/utf8/utf8_encoded_length.js +36 -0
  362. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +417 -0
  363. package/src/core/color/COLOR_REVIEW_2026_08_28.md +779 -0
  364. package/src/core/color/Color.d.ts +128 -20
  365. package/src/core/color/Color.d.ts.map +1 -1
  366. package/src/core/color/Color.js +959 -866
  367. package/src/core/color/PQ/PQ_constants.d.ts +10 -0
  368. package/src/core/color/PQ/PQ_constants.d.ts.map +1 -1
  369. package/src/core/color/PQ/PQ_constants.js +10 -0
  370. package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -1
  371. package/src/core/color/PQ/linear_to_PQ.js +6 -0
  372. package/src/core/color/REC709_PRIMARIES.d.ts +50 -0
  373. package/src/core/color/REC709_PRIMARIES.d.ts.map +1 -0
  374. package/src/core/color/REC709_PRIMARIES.js +53 -0
  375. package/src/core/color/construct/color_from_hex.d.ts +21 -0
  376. package/src/core/color/construct/color_from_hex.d.ts.map +1 -0
  377. package/src/core/color/construct/color_from_hex.js +33 -0
  378. package/src/core/color/construct/color_from_temperature.d.ts +22 -0
  379. package/src/core/color/construct/color_from_temperature.d.ts.map +1 -0
  380. package/src/core/color/construct/color_from_temperature.js +33 -0
  381. package/src/core/color/construct/color_from_uint24.d.ts +19 -0
  382. package/src/core/color/construct/color_from_uint24.d.ts.map +1 -0
  383. package/src/core/color/construct/color_from_uint24.js +26 -0
  384. package/src/core/color/construct/color_from_uint32.d.ts +20 -0
  385. package/src/core/color/construct/color_from_uint32.d.ts.map +1 -0
  386. package/src/core/color/construct/color_from_uint32.js +39 -0
  387. package/src/core/color/construct/color_gray.d.ts +17 -0
  388. package/src/core/color/construct/color_gray.d.ts.map +1 -0
  389. package/src/core/color/construct/color_gray.js +22 -0
  390. package/src/core/color/hex/hex2rgb.d.ts +1 -11
  391. package/src/core/color/hex/hex2rgb.d.ts.map +1 -1
  392. package/src/core/color/hex/hex2rgb.js +7 -24
  393. package/src/core/color/hex/hex_to_rgb.d.ts +18 -0
  394. package/src/core/color/hex/hex_to_rgb.d.ts.map +1 -0
  395. package/src/core/color/hex/hex_to_rgb.js +62 -0
  396. package/src/core/color/hex/rgb2hex.d.ts +1 -8
  397. package/src/core/color/hex/rgb2hex.d.ts.map +1 -1
  398. package/src/core/color/hex/rgb2hex.js +7 -13
  399. package/src/core/color/hex/rgb_to_hex.d.ts +9 -0
  400. package/src/core/color/hex/rgb_to_hex.d.ts.map +1 -0
  401. package/src/core/color/hex/rgb_to_hex.js +13 -0
  402. package/src/core/color/hex/rgba_to_hex.d.ts +19 -0
  403. package/src/core/color/hex/rgba_to_hex.d.ts.map +1 -0
  404. package/src/core/color/hex/rgba_to_hex.js +23 -0
  405. package/src/core/color/hsv/hsv2rgb.d.ts +1 -12
  406. package/src/core/color/hsv/hsv2rgb.d.ts.map +1 -1
  407. package/src/core/color/hsv/hsv2rgb.js +8 -21
  408. package/src/core/color/hsv/hsv2rgb_float.d.ts +1 -12
  409. package/src/core/color/hsv/hsv2rgb_float.d.ts.map +1 -1
  410. package/src/core/color/hsv/hsv2rgb_float.js +8 -56
  411. package/src/core/color/hsv/hsv_to_rgb.d.ts +13 -0
  412. package/src/core/color/hsv/hsv_to_rgb.d.ts.map +1 -0
  413. package/src/core/color/hsv/hsv_to_rgb.js +39 -0
  414. package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts +13 -0
  415. package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts.map +1 -0
  416. package/src/core/color/hsv/hsv_to_rgb_uint8.js +21 -0
  417. package/src/core/color/hsv/hue_to_rgb_sextant.d.ts +24 -0
  418. package/src/core/color/hsv/hue_to_rgb_sextant.d.ts.map +1 -0
  419. package/src/core/color/hsv/hue_to_rgb_sextant.js +67 -0
  420. package/src/core/color/hsv/rgb2hsv.d.ts +1 -12
  421. package/src/core/color/hsv/rgb2hsv.d.ts.map +1 -1
  422. package/src/core/color/hsv/rgb2hsv.js +7 -42
  423. package/src/core/color/hsv/rgb_to_hsv.d.ts +13 -0
  424. package/src/core/color/hsv/rgb_to_hsv.d.ts.map +1 -0
  425. package/src/core/color/hsv/rgb_to_hsv.js +42 -0
  426. package/src/core/color/hunt/hpe_to_xyz.d.ts +2 -0
  427. package/src/core/color/hunt/hpe_to_xyz.d.ts.map +1 -0
  428. package/src/core/color/hunt/hpe_to_xyz.js +4 -0
  429. package/src/core/color/hunt/xyz_to_hpe.d.ts +1 -6
  430. package/src/core/color/hunt/xyz_to_hpe.d.ts.map +1 -1
  431. package/src/core/color/hunt/xyz_to_hpe.js +12 -15
  432. package/src/core/color/illuminant/D65_spd_tabulated.d.ts.map +1 -1
  433. package/src/core/color/illuminant/D65_spd_tabulated.js +107 -104
  434. package/src/core/color/int2rgb.d.ts +1 -10
  435. package/src/core/color/int2rgb.d.ts.map +1 -1
  436. package/src/core/color/int2rgb.js +7 -12
  437. package/src/core/color/int_to_rgb.d.ts +21 -0
  438. package/src/core/color/int_to_rgb.d.ts.map +1 -0
  439. package/src/core/color/int_to_rgb.js +33 -0
  440. package/src/core/color/kelvin/kelvin_to_rgb.d.ts.map +1 -1
  441. package/src/core/color/kelvin/kelvin_to_rgb.js +22 -2
  442. package/src/core/color/kelvin/rgb_to_kelvin.d.ts +22 -2
  443. package/src/core/color/kelvin/rgb_to_kelvin.d.ts.map +1 -1
  444. package/src/core/color/kelvin/rgb_to_kelvin.js +22 -2
  445. package/src/core/color/lab/lab_to_xyz.d.ts +20 -0
  446. package/src/core/color/lab/lab_to_xyz.d.ts.map +1 -0
  447. package/src/core/color/lab/lab_to_xyz.js +69 -0
  448. package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts +34 -0
  449. package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts.map +1 -0
  450. package/src/core/color/oklab/gamut_clip_adaptive_l0.js +46 -0
  451. package/src/core/color/oklab/gamut_clip_at_l0.d.ts +33 -0
  452. package/src/core/color/oklab/gamut_clip_at_l0.d.ts.map +1 -0
  453. package/src/core/color/oklab/gamut_clip_at_l0.js +84 -0
  454. package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts +24 -0
  455. package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts.map +1 -0
  456. package/src/core/color/oklab/gamut_clip_preserve_chroma.js +37 -0
  457. package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts +23 -0
  458. package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts.map +1 -0
  459. package/src/core/color/oklab/gamut_clip_project_to_lcusp.js +46 -0
  460. package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts +19 -0
  461. package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts.map +1 -0
  462. package/src/core/color/oklab/linear_srgb_to_okhsl.js +93 -0
  463. package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts +12 -4
  464. package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts.map +1 -1
  465. package/src/core/color/oklab/linear_srgb_to_okhsv.js +108 -84
  466. package/src/core/color/oklab/linear_srgb_to_oklab.d.ts +13 -4
  467. package/src/core/color/oklab/linear_srgb_to_oklab.d.ts.map +1 -1
  468. package/src/core/color/oklab/linear_srgb_to_oklab.js +28 -19
  469. package/src/core/color/oklab/okhsl_chroma_bounds.d.ts +51 -0
  470. package/src/core/color/oklab/okhsl_chroma_bounds.d.ts.map +1 -0
  471. package/src/core/color/oklab/okhsl_chroma_bounds.js +128 -0
  472. package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts +19 -0
  473. package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts.map +1 -0
  474. package/src/core/color/oklab/okhsl_to_linear_srgb.js +85 -0
  475. package/src/core/color/oklab/oklab_to_oklch.d.ts +24 -0
  476. package/src/core/color/oklab/oklab_to_oklch.d.ts.map +1 -0
  477. package/src/core/color/oklab/oklab_to_oklch.js +35 -0
  478. package/src/core/color/oklab/oklch_to_oklab.d.ts +18 -0
  479. package/src/core/color/oklab/oklch_to_oklab.d.ts.map +1 -0
  480. package/src/core/color/oklab/oklch_to_oklab.js +26 -0
  481. package/src/core/color/oklab/toe.d.ts +12 -0
  482. package/src/core/color/oklab/toe.d.ts.map +1 -1
  483. package/src/core/color/oklab/toe.js +34 -22
  484. package/src/core/color/operations/color_add.d.ts +21 -0
  485. package/src/core/color/operations/color_add.d.ts.map +1 -0
  486. package/src/core/color/operations/color_add.js +23 -0
  487. package/src/core/color/operations/color_add_scaled.d.ts +20 -0
  488. package/src/core/color/operations/color_add_scaled.d.ts.map +1 -0
  489. package/src/core/color/operations/color_add_scaled.js +30 -0
  490. package/src/core/color/operations/color_clamp.d.ts +22 -0
  491. package/src/core/color/operations/color_clamp.d.ts.map +1 -0
  492. package/src/core/color/operations/color_clamp.js +25 -0
  493. package/src/core/color/operations/color_darken.d.ts +2 -1
  494. package/src/core/color/operations/color_darken.d.ts.map +1 -1
  495. package/src/core/color/operations/color_darken.js +25 -24
  496. package/src/core/color/operations/color_desaturate.d.ts +2 -1
  497. package/src/core/color/operations/color_desaturate.d.ts.map +1 -1
  498. package/src/core/color/operations/color_desaturate.js +25 -24
  499. package/src/core/color/operations/color_get_hsl.d.ts +18 -0
  500. package/src/core/color/operations/color_get_hsl.d.ts.map +1 -0
  501. package/src/core/color/operations/color_get_hsl.js +60 -0
  502. package/src/core/color/operations/color_is_in_gamut.d.ts +20 -0
  503. package/src/core/color/operations/color_is_in_gamut.d.ts.map +1 -0
  504. package/src/core/color/operations/color_is_in_gamut.js +30 -0
  505. package/src/core/color/operations/color_lerp.d.ts +12 -3
  506. package/src/core/color/operations/color_lerp.d.ts.map +1 -1
  507. package/src/core/color/operations/color_lerp.js +55 -46
  508. package/src/core/color/operations/color_lighten.d.ts +9 -5
  509. package/src/core/color/operations/color_lighten.d.ts.map +1 -1
  510. package/src/core/color/operations/color_lighten.js +29 -24
  511. package/src/core/color/operations/color_mix_okhsv_channel.d.ts +24 -0
  512. package/src/core/color/operations/color_mix_okhsv_channel.d.ts.map +1 -0
  513. package/src/core/color/operations/color_mix_okhsv_channel.js +48 -0
  514. package/src/core/color/operations/color_multiply_rgb.d.ts +21 -0
  515. package/src/core/color/operations/color_multiply_rgb.d.ts.map +1 -0
  516. package/src/core/color/operations/color_multiply_rgb.js +26 -0
  517. package/src/core/color/operations/color_saturate.d.ts +9 -5
  518. package/src/core/color/operations/color_saturate.d.ts.map +1 -1
  519. package/src/core/color/operations/color_saturate.js +29 -24
  520. package/src/core/color/operations/color_scale_okhsv_channel.d.ts +1 -1
  521. package/src/core/color/operations/color_scale_okhsv_channel.d.ts.map +1 -1
  522. package/src/core/color/operations/color_scale_okhsv_channel.js +58 -45
  523. package/src/core/color/operations/color_srgb_apply.d.ts +28 -0
  524. package/src/core/color/operations/color_srgb_apply.d.ts.map +1 -0
  525. package/src/core/color/operations/color_srgb_apply.js +40 -0
  526. package/src/core/color/operations/color_sub.d.ts +16 -0
  527. package/src/core/color/operations/color_sub.d.ts.map +1 -0
  528. package/src/core/color/operations/color_sub.js +18 -0
  529. package/src/core/color/parse_color.d.ts +10 -1
  530. package/src/core/color/parse_color.d.ts.map +1 -1
  531. package/src/core/color/parse_color.js +100 -84
  532. package/src/core/color/parse_color_normalized.d.ts +24 -0
  533. package/src/core/color/parse_color_normalized.d.ts.map +1 -0
  534. package/src/core/color/parse_color_normalized.js +38 -0
  535. package/src/core/color/rgb2uint24.d.ts +1 -8
  536. package/src/core/color/rgb2uint24.d.ts.map +1 -1
  537. package/src/core/color/rgb2uint24.js +7 -20
  538. package/src/core/color/rgb2uint32.d.ts +1 -9
  539. package/src/core/color/rgb2uint32.d.ts.map +1 -1
  540. package/src/core/color/rgb2uint32.js +7 -18
  541. package/src/core/color/rgb_to_luminance.d.ts +14 -5
  542. package/src/core/color/rgb_to_luminance.d.ts.map +1 -1
  543. package/src/core/color/rgb_to_luminance.js +24 -13
  544. package/src/core/color/rgb_to_uint24.d.ts +9 -0
  545. package/src/core/color/rgb_to_uint24.d.ts.map +1 -0
  546. package/src/core/color/rgb_to_uint24.js +20 -0
  547. package/src/core/color/rgb_to_uint32.d.ts +10 -0
  548. package/src/core/color/rgb_to_uint32.d.ts.map +1 -0
  549. package/src/core/color/rgb_to_uint32.js +21 -0
  550. package/src/core/color/sRGB/linear_to_sRGB.d.ts +7 -0
  551. package/src/core/color/sRGB/linear_to_sRGB.d.ts.map +1 -1
  552. package/src/core/color/sRGB/linear_to_sRGB.js +39 -32
  553. package/src/core/color/xyz/hpe_to_xyz.d.ts +24 -0
  554. package/src/core/color/xyz/hpe_to_xyz.d.ts.map +1 -0
  555. package/src/core/color/xyz/hpe_to_xyz.js +39 -0
  556. package/src/core/color/xyz/rgb_to_xyz.d.ts +17 -2
  557. package/src/core/color/xyz/rgb_to_xyz.d.ts.map +1 -1
  558. package/src/core/color/xyz/rgb_to_xyz.js +44 -18
  559. package/src/core/color/xyz/xyz_cmf_tabulated.d.ts.map +1 -1
  560. package/src/core/color/xyz/xyz_cmf_tabulated.js +217 -211
  561. package/src/core/color/xyz/xyz_cmf_wyman.d.ts +4 -0
  562. package/src/core/color/xyz/xyz_cmf_wyman.d.ts.map +1 -1
  563. package/src/core/color/xyz/xyz_cmf_wyman.js +74 -65
  564. package/src/core/color/xyz/xyz_to_hpe.d.ts +18 -0
  565. package/src/core/color/xyz/xyz_to_hpe.d.ts.map +1 -0
  566. package/src/core/color/xyz/xyz_to_hpe.js +33 -0
  567. package/src/core/color/xyz/xyz_to_rgb.d.ts +17 -2
  568. package/src/core/color/xyz/xyz_to_rgb.d.ts.map +1 -1
  569. package/src/core/color/xyz/xyz_to_rgb.js +39 -16
  570. package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts +7 -2
  571. package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts.map +1 -1
  572. package/src/core/color/ycxcz/xyz_to_ycxcz.js +33 -28
  573. package/src/core/events/signal/Signal.js +820 -820
  574. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  575. package/src/core/geom/3d/shape/ConvexHullShape3D.js +28 -4
  576. package/src/core/math/clamp_index.d.ts +18 -0
  577. package/src/core/math/clamp_index.d.ts.map +1 -0
  578. package/src/core/math/clamp_index.js +23 -0
  579. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +803 -0
  580. package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts +5 -3
  581. package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts.map +1 -1
  582. package/src/engine/asset/loaders/image/jpeg/JpegFrame.js +5 -3
  583. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts +2 -1
  584. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts.map +1 -1
  585. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.js +2 -1
  586. package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts +17 -0
  587. package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts.map +1 -1
  588. package/src/engine/asset/loaders/image/jpeg/JpegImage.js +197 -157
  589. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts +0 -1
  590. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts.map +1 -1
  591. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.js +0 -1
  592. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts +28 -0
  593. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts.map +1 -0
  594. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.js +31 -0
  595. package/src/engine/asset/loaders/image/png/PNG.d.ts +3 -18
  596. package/src/engine/asset/loaders/image/png/PNG.d.ts.map +1 -1
  597. package/src/engine/asset/loaders/image/png/PNG.js +3 -139
  598. package/src/engine/asset/loaders/image/png/PNGReader.d.ts +16 -16
  599. package/src/engine/asset/loaders/image/png/PNGReader.d.ts.map +1 -1
  600. package/src/engine/asset/loaders/image/png/PNGReader.js +143 -160
  601. package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.js +3 -3
  602. package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.js +3 -3
  603. package/src/engine/asset/loaders/image/png/crc32.d.ts +1 -15
  604. package/src/engine/asset/loaders/image/png/crc32.d.ts.map +1 -1
  605. package/src/engine/asset/loaders/image/png/crc32.js +5 -89
  606. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts +12 -10
  607. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts.map +1 -1
  608. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +20 -45
  609. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts +2 -1
  610. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts.map +1 -1
  611. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +2 -0
  612. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts +2 -1
  613. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts.map +1 -1
  614. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +2 -0
  615. package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts +22 -0
  616. package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts.map +1 -0
  617. package/src/engine/asset/loaders/image/png/filter/png_unfilter.js +68 -0
  618. package/src/engine/asset/loaders/image/png/png_inflate.d.ts +20 -0
  619. package/src/engine/asset/loaders/image/png/png_inflate.d.ts.map +1 -0
  620. package/src/engine/asset/loaders/image/png/png_inflate.js +85 -0
  621. package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts +22 -0
  622. package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts.map +1 -0
  623. package/src/engine/asset/loaders/image/png/png_unpack_samples.js +38 -0
  624. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  625. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +135 -132
  626. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +16 -3
  627. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  628. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1157 -1149
  629. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +26 -0
  630. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
  631. package/src/engine/graphics/render/frame_graph/FrameGraph.js +947 -900
  632. package/src/engine/physics/ecs/PhysicsSystem.d.ts +69 -81
  633. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  634. package/src/engine/physics/ecs/PhysicsSystem.js +83 -77
  635. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  636. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +451 -0
  637. package/src/shade/device/ShadeGPUCommandContext.d.ts +21 -0
  638. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  639. package/src/shade/device/ShadeGPUCommandContext.js +940 -749
  640. package/src/shade/device/timing/GPUTimerArray.d.ts +22 -4
  641. package/src/shade/device/timing/GPUTimerArray.d.ts.map +1 -1
  642. package/src/shade/device/timing/GPUTimerArray.js +88 -14
  643. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +876 -0
  644. package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts +93 -0
  645. package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts.map +1 -0
  646. package/src/shade/device/timing/profile/GPUFrameRecorder.js +297 -0
  647. package/src/shade/device/timing/profile/GPUProfileCapture.d.ts +62 -0
  648. package/src/shade/device/timing/profile/GPUProfileCapture.d.ts.map +1 -0
  649. package/src/shade/device/timing/profile/GPUProfileCapture.js +77 -0
  650. package/src/shade/device/timing/profile/GPUProfileFrame.d.ts +73 -0
  651. package/src/shade/device/timing/profile/GPUProfileFrame.d.ts.map +1 -0
  652. package/src/shade/device/timing/profile/GPUProfileFrame.js +88 -0
  653. package/src/shade/device/timing/profile/GPUProfileLevel.d.ts +20 -0
  654. package/src/shade/device/timing/profile/GPUProfileLevel.d.ts.map +1 -0
  655. package/src/shade/device/timing/profile/GPUProfileLevel.js +50 -0
  656. package/src/shade/device/timing/profile/GPUProfileMeta.d.ts +63 -0
  657. package/src/shade/device/timing/profile/GPUProfileMeta.d.ts.map +1 -0
  658. package/src/shade/device/timing/profile/GPUProfileMeta.js +73 -0
  659. package/src/shade/device/timing/profile/GPUProfileSession.d.ts +173 -0
  660. package/src/shade/device/timing/profile/GPUProfileSession.d.ts.map +1 -0
  661. package/src/shade/device/timing/profile/GPUProfileSession.js +470 -0
  662. package/src/shade/device/timing/profile/GPUProfileSpan.d.ts +60 -0
  663. package/src/shade/device/timing/profile/GPUProfileSpan.d.ts.map +1 -0
  664. package/src/shade/device/timing/profile/GPUProfileSpan.js +65 -0
  665. package/src/shade/device/timing/profile/GPUProfileSpanKind.d.ts +19 -0
  666. package/src/shade/device/timing/profile/GPUProfileSpanKind.d.ts.map +1 -0
  667. package/src/shade/device/timing/profile/GPUProfileSpanKind.js +25 -0
  668. package/src/shade/device/timing/profile/GPUProfileTopology.d.ts +159 -0
  669. package/src/shade/device/timing/profile/GPUProfileTopology.d.ts.map +1 -0
  670. package/src/shade/device/timing/profile/GPUProfileTopology.js +202 -0
  671. package/src/shade/device/timing/profile/GPUProfileWork.d.ts +90 -0
  672. package/src/shade/device/timing/profile/GPUProfileWork.d.ts.map +1 -0
  673. package/src/shade/device/timing/profile/GPUProfileWork.js +115 -0
  674. package/src/shade/device/timing/profile/SGPTDefect.d.ts +43 -0
  675. package/src/shade/device/timing/profile/SGPTDefect.d.ts.map +1 -0
  676. package/src/shade/device/timing/profile/SGPTDefect.js +65 -0
  677. package/src/shade/device/timing/profile/SGPTHeader.d.ts +38 -0
  678. package/src/shade/device/timing/profile/SGPTHeader.d.ts.map +1 -0
  679. package/src/shade/device/timing/profile/SGPTHeader.js +46 -0
  680. package/src/shade/device/timing/profile/SGPTRecordRef.d.ts +37 -0
  681. package/src/shade/device/timing/profile/SGPTRecordRef.d.ts.map +1 -0
  682. package/src/shade/device/timing/profile/SGPTRecordRef.js +45 -0
  683. package/src/shade/device/timing/profile/SGPTWriteStream.d.ts +103 -0
  684. package/src/shade/device/timing/profile/SGPTWriteStream.d.ts.map +1 -0
  685. package/src/shade/device/timing/profile/SGPTWriteStream.js +287 -0
  686. package/src/shade/device/timing/profile/SGPT_DEFECT.d.ts +19 -0
  687. package/src/shade/device/timing/profile/SGPT_DEFECT.d.ts.map +1 -0
  688. package/src/shade/device/timing/profile/SGPT_DEFECT.js +26 -0
  689. package/src/shade/device/timing/profile/SGPT_FLAG.d.ts +8 -0
  690. package/src/shade/device/timing/profile/SGPT_FLAG.d.ts.map +1 -0
  691. package/src/shade/device/timing/profile/SGPT_FLAG.js +15 -0
  692. package/src/shade/device/timing/profile/SGPT_FORMAT.md +345 -0
  693. package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.d.ts +10 -0
  694. package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.d.ts.map +1 -0
  695. package/src/shade/device/timing/profile/SGPT_FORMAT_VERSION.js +9 -0
  696. package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.d.ts +17 -0
  697. package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.d.ts.map +1 -0
  698. package/src/shade/device/timing/profile/SGPT_FRAME_SLACK.js +16 -0
  699. package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.d.ts +16 -0
  700. package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.d.ts.map +1 -0
  701. package/src/shade/device/timing/profile/SGPT_HEADER_CHECKSUM_COVERAGE.js +15 -0
  702. package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.d.ts +15 -0
  703. package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.d.ts.map +1 -0
  704. package/src/shade/device/timing/profile/SGPT_HEADER_OFFSET.js +29 -0
  705. package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.d.ts +7 -0
  706. package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.d.ts.map +1 -0
  707. package/src/shade/device/timing/profile/SGPT_HEADER_SIZE.js +6 -0
  708. package/src/shade/device/timing/profile/SGPT_MAGIC.d.ts +7 -0
  709. package/src/shade/device/timing/profile/SGPT_MAGIC.d.ts.map +1 -0
  710. package/src/shade/device/timing/profile/SGPT_MAGIC.js +6 -0
  711. package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.d.ts +16 -0
  712. package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.d.ts.map +1 -0
  713. package/src/shade/device/timing/profile/SGPT_MIN_READER_VERSION.js +15 -0
  714. package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.d.ts +7 -0
  715. package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.d.ts.map +1 -0
  716. package/src/shade/device/timing/profile/SGPT_RECORD_HEADER_SIZE.js +6 -0
  717. package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.d.ts +13 -0
  718. package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.d.ts.map +1 -0
  719. package/src/shade/device/timing/profile/SGPT_RECORD_OFFSET.js +23 -0
  720. package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.d.ts +12 -0
  721. package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.d.ts.map +1 -0
  722. package/src/shade/device/timing/profile/SGPT_RECORD_SYNC.js +11 -0
  723. package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.d.ts +18 -0
  724. package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.d.ts.map +1 -0
  725. package/src/shade/device/timing/profile/SGPT_RECORD_TYPE.js +46 -0
  726. package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts +20 -0
  727. package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts.map +1 -0
  728. package/src/shade/device/timing/profile/frame_graph_extract_topology.js +174 -0
  729. package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts +21 -0
  730. package/src/shade/device/timing/profile/make_profiling_pass_encoder.d.ts.map +1 -0
  731. package/src/shade/device/timing/profile/make_profiling_pass_encoder.js +114 -0
  732. package/src/shade/device/timing/profile/parse_workgroup_size.d.ts +20 -0
  733. package/src/shade/device/timing/profile/parse_workgroup_size.d.ts.map +1 -0
  734. package/src/shade/device/timing/profile/parse_workgroup_size.js +43 -0
  735. package/src/shade/device/timing/profile/sgpt_fourcc.d.ts +18 -0
  736. package/src/shade/device/timing/profile/sgpt_fourcc.d.ts.map +1 -0
  737. package/src/shade/device/timing/profile/sgpt_fourcc.js +34 -0
  738. package/src/shade/device/timing/profile/sgpt_frame_codec.d.ts +42 -0
  739. package/src/shade/device/timing/profile/sgpt_frame_codec.d.ts.map +1 -0
  740. package/src/shade/device/timing/profile/sgpt_frame_codec.js +172 -0
  741. package/src/shade/device/timing/profile/sgpt_meta_codec.d.ts +20 -0
  742. package/src/shade/device/timing/profile/sgpt_meta_codec.d.ts.map +1 -0
  743. package/src/shade/device/timing/profile/sgpt_meta_codec.js +65 -0
  744. package/src/shade/device/timing/profile/sgpt_read_capture.d.ts +19 -0
  745. package/src/shade/device/timing/profile/sgpt_read_capture.d.ts.map +1 -0
  746. package/src/shade/device/timing/profile/sgpt_read_capture.js +134 -0
  747. package/src/shade/device/timing/profile/sgpt_read_header.d.ts +22 -0
  748. package/src/shade/device/timing/profile/sgpt_read_header.d.ts.map +1 -0
  749. package/src/shade/device/timing/profile/sgpt_read_header.js +94 -0
  750. package/src/shade/device/timing/profile/sgpt_read_records.d.ts +27 -0
  751. package/src/shade/device/timing/profile/sgpt_read_records.d.ts.map +1 -0
  752. package/src/shade/device/timing/profile/sgpt_read_records.js +153 -0
  753. package/src/shade/device/timing/profile/sgpt_topology_codec.d.ts +54 -0
  754. package/src/shade/device/timing/profile/sgpt_topology_codec.d.ts.map +1 -0
  755. package/src/shade/device/timing/profile/sgpt_topology_codec.js +329 -0
  756. package/src/shade/device/timing/profile/sgpt_write_header.d.ts +22 -0
  757. package/src/shade/device/timing/profile/sgpt_write_header.d.ts.map +1 -0
  758. package/src/shade/device/timing/profile/sgpt_write_header.js +47 -0
  759. package/src/shade/device/timing/profile/sgpt_write_record.d.ts +23 -0
  760. package/src/shade/device/timing/profile/sgpt_write_record.d.ts.map +1 -0
  761. package/src/shade/device/timing/profile/sgpt_write_record.js +62 -0
  762. package/src/shade/renderer/Renderer.d.ts +21 -0
  763. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  764. package/src/shade/renderer/Renderer.js +2513 -2455
  765. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  766. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts +14 -3
  767. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts.map +1 -1
  768. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.js +50 -51
  769. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  770. package/src/shade/renderer/scene/serialization/deserialize_scene.js +531 -521
  771. package/src/shade/renderer/scene/serialization/write_image_source.d.ts.map +1 -1
  772. package/src/shade/renderer/scene/serialization/write_image_source.js +6 -4
  773. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.d.ts.map +1 -1
  774. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.js +34 -32
  775. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +31 -0
  776. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts.map +1 -1
  777. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -31
  778. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts +10 -0
  779. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts.map +1 -1
  780. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.js +32 -22
  781. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.d.ts.map +1 -1
  782. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.js +22 -21
  783. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +6 -0
  784. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
  785. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +110 -104
  786. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.d.ts.map +1 -1
  787. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.js +23 -21
  788. package/src/view/elements/ColorPickerView.js +296 -296
  789. package/src/avif/codec/dec/Readme.md +0 -14
  790. package/src/avif/codec/dec/avif_dec.cpp +0 -234
  791. package/src/avif/codec/dec/avif_dec.d.ts +0 -3
  792. package/src/avif/codec/dec/avif_dec.d.ts.map +0 -1
  793. package/src/avif/codec/dec/avif_dec.js +0 -16
  794. package/src/avif/codec/dec/avif_dec.wasm +0 -0
  795. package/src/avif/codec/enc/README.md +0 -37
  796. package/src/avif/codec/enc/avif_enc.cpp +0 -216
  797. package/src/avif/codec/enc/avif_enc.d.ts +0 -3
  798. package/src/avif/codec/enc/avif_enc.d.ts.map +0 -1
  799. package/src/avif/codec/enc/avif_enc.js +0 -6144
  800. package/src/avif/codec/enc/avif_enc.wasm +0 -0
  801. package/src/avif/codec/enc/avif_enc_mt.d.ts +0 -3
  802. package/src/avif/codec/enc/avif_enc_mt.d.ts.map +0 -1
  803. package/src/avif/codec/enc/avif_enc_mt.js +0 -6811
  804. package/src/avif/codec/enc/avif_enc_mt.wasm +0 -0
  805. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts +0 -2
  806. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts.map +0 -1
  807. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts +0 -11
  808. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts.map +0 -1
  809. package/src/avif/codec/enc/avif_enc_mt.worker.js +0 -171
  810. package/src/avif/codec/enc/avif_enc_mt.worker.mjs +0 -1
  811. package/src/avif/decode.d.ts +0 -3
  812. package/src/avif/decode.d.ts.map +0 -1
  813. package/src/avif/decode.js +0 -42
  814. package/src/avif/encode.d.ts +0 -3
  815. package/src/avif/encode.d.ts.map +0 -1
  816. package/src/avif/encode.js +0 -75
  817. package/src/avif/meta.d.ts +0 -24
  818. package/src/avif/meta.d.ts.map +0 -1
  819. package/src/avif/meta.js +0 -23
  820. package/src/avif/tsconfig.tsbuildinfo +0 -1
  821. package/src/avif/utils.d.ts +0 -17
  822. package/src/avif/utils.d.ts.map +0 -1
  823. package/src/avif/utils.js +0 -31
  824. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts +0 -23
  825. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts.map +0 -1
  826. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.js +0 -214
  827. package/src/engine/asset/loaders/image/png/inflate.d.ts +0 -7
  828. package/src/engine/asset/loaders/image/png/inflate.d.ts.map +0 -1
  829. package/src/engine/asset/loaders/image/png/inflate.js +0 -20
  830. package/src/engine/save/GameStateLoader.d.ts +0 -49
  831. package/src/engine/save/GameStateLoader.d.ts.map +0 -1
  832. package/src/engine/save/GameStateLoader.js +0 -168
@@ -1,901 +1,948 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { float_to_uint8 } from "../../../../core/binary/float_to_uint8.js";
3
- import LineBuilder from "../../../../core/codegen/LineBuilder.js";
4
- import { rgb2hex } from "../../../../core/color/hex/rgb2hex.js";
5
- import { okhsv_to_linear_srgb } from "../../../../core/color/oklab/okhsv_to_linear_srgb.js";
6
- import { Signal } from "../../../../core/events/signal/Signal.js";
7
- import { noop } from "../../../../core/function/noop.js";
8
- import { graphviz_escape_string } from "../../../../core/graph/format/graphviz/graphviz_escape_string.js";
9
- import { lerp } from "../../../../core/math/lerp.js";
10
- import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
11
- import { FrameGraphScope } from "./FrameGraphScope.js";
12
- import { FrameGraphState, frame_graph_state_name } from "./FrameGraphState.js";
13
- import { FramePassBuilder } from "./FramePassBuilder.js";
14
- import { FramePassNode } from "./FramePassNode.js";
15
- import { FramePassResources } from "./FramePassResources.js";
16
- import { ResourceEntry } from "./ResourceEntry.js";
17
- import { ResourceNode } from "./ResourceNode.js";
18
-
19
- /**
20
- * A high-level rendering architecture system that manages render passes and their resource dependencies.
21
- * This class schedules frame execution by building a directed graph of resources (textures, buffers) and the passes that read or write to them.
22
- *
23
- * By deferring execution until after the graph is compiled, it automatically handles complex tasks such as:
24
- * - Culling unreferenced resources and passes (dead code elimination for rendering).
25
- * - Automatic resource lifetime management (allocating transient resources just-in-time and destroying them immediately after their last use).
26
- * - Visualizing the rendering pipeline via GraphViz DOT exports.
27
- *
28
- * The graph is **one-shot**: it is recorded into, then compiled, then executed, and it never goes
29
- * back. {@link compile} derives reference counts and resource lifetimes from what was recorded, so
30
- * recording into a compiled graph corrupts them, and compiling twice accumulates resource reference
31
- * counts instead of resetting them, which disables culling entirely. Build a new graph per frame.
32
- *
33
- * @see FrameGraphState
34
- *
35
- * @source Based on the Frostbite's GDC paper "FrameGraph: Extensible Rendering Architecture in Frostbite" by Yuriy O'Donnell
36
- * @example
37
- * const graph = new FrameGraph("My Graph");
38
- *
39
- * const pass_data = {};
40
- * const pass = graph.add("GBuffer Pass", pass_data, (data, resources, context) => {
41
- *
42
- * context.beginRenderPass({
43
- * depth_attachment: resources.get(data.depth),
44
- * color_attachments:[
45
- * resources.get(data.normal),
46
- * resources.get(data.albedo),
47
- * ]
48
- * });
49
- *
50
- * for (const renderable in renderables){
51
- * drawMesh(context, renderable.mesh, renderable.material);
52
- * }
53
- *
54
- * context.endRenderPass();
55
- *
56
- * });
57
- *
58
- * pass_data.depth = pass.create("depth", {...});
59
- * pass_data.albedo = pass.create("albedo", {...});
60
- * pass_data.normal = pass.create("normal", {...});
61
- *
62
- * graph.compile();
63
- * graph.execute(context);
64
- *
65
- */
66
- export class FrameGraph {
67
- /**
68
- * Human-readable name, used for debugging and UI primarily.
69
- * There is no uniqueness guarantee
70
- * @type {string}
71
- */
72
- name = "";
73
-
74
- /**
75
- *
76
- * @type {Signal<IFrameGraphContext,this>}
77
- */
78
- onExecuted = new Signal();
79
-
80
- /**
81
- *
82
- * @type {FramePassNode[]}
83
- * @private
84
- */
85
- __pass_nodes = [];
86
-
87
- /**
88
- *
89
- * @type {ResourceNode[]}
90
- * @private
91
- */
92
- __resource_nodes = [];
93
-
94
- /**
95
- *
96
- * @type {ResourceEntry[]}
97
- * @private
98
- */
99
- __resource_registry = [];
100
-
101
- /**
102
- * Position in the one-shot lifecycle.
103
- * @type {FrameGraphState}
104
- */
105
- #state = FrameGraphState.Recording;
106
-
107
- /**
108
- * Innermost open recording scope, or null. The enclosing scopes are its
109
- * {@link FrameGraphScope.parent} chain, so this one field is the whole stack.
110
- * @type {FrameGraphScope|null}
111
- */
112
- #scope = null;
113
-
114
- /**
115
- * Scopes opened so far, which is what gives each one an ID unique within this graph.
116
- * @type {number}
117
- */
118
- #scope_count = 0;
119
-
120
- /**
121
- *
122
- * @param {string} name
123
- */
124
- constructor(name = "") {
125
- assert.isString(name, 'name');
126
-
127
-
128
- this.name = name;
129
- }
130
-
131
- /**
132
- * Position in the one-shot lifecycle. Recording is only legal in
133
- * {@link FrameGraphState.Recording}, and {@link execute} is only legal in
134
- * {@link FrameGraphState.Compiled}.
135
- * @returns {FrameGraphState}
136
- */
137
- get state() {
138
- return this.#state;
139
- }
140
-
141
- /**
142
- * Innermost open recording scope, or null when recording outside of one.
143
- * @returns {FrameGraphScope|null}
144
- */
145
- get scope() {
146
- return this.#scope;
147
- }
148
-
149
- /**
150
- * Open a recording scope. Every pass and resource node created until the matching
151
- * {@link pop_scope} is stamped with it, nested scopes included.
152
- *
153
- * This is how the graph attributes work to whoever recorded it. Node names come from the
154
- * helpers that happened to build them and say nothing about the caller, so without a scope a
155
- * DOT export cannot group a frame by the thing each group belongs to, and a diagnostic cannot
156
- * ask whether everything one caller recorded was culled.
157
- *
158
- * @param {string} name
159
- * @returns {FrameGraphScope} the scope opened, for later queries against node stamps
160
- */
161
- push_scope(name) {
162
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': scopes can only be opened while recording, graph is ${frame_graph_state_name(this.#state)}`);
163
- assert.isString(name, 'name');
164
-
165
- const scope = new FrameGraphScope();
166
-
167
- scope.name = name;
168
- scope.id = this.#scope_count++;
169
- scope.parent = this.#scope;
170
-
171
- this.#scope = scope;
172
-
173
- return scope;
174
- }
175
-
176
- /**
177
- * Close the innermost open recording scope, returning to the one that encloses it.
178
- */
179
- pop_scope() {
180
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': scopes can only be closed while recording, graph is ${frame_graph_state_name(this.#state)}`);
181
- assert.notNull(this.#scope, `FrameGraph '${this.name}': pop_scope() without a scope open`);
182
-
183
- this.#scope = this.#scope.parent;
184
- }
185
-
186
- /**
187
- *
188
- * @param {number} id Resource Node ID
189
- * @returns {ResourceEntry}
190
- */
191
- getResourceEntry(id) {
192
- const node = this.getResourceNode(id);
193
-
194
- assert.defined(node, 'node');
195
-
196
- const registry = this.__resource_registry;
197
-
198
- const entry = registry[node.resource_id];
199
-
200
- assert.defined(entry, 'entry');
201
-
202
- return entry;
203
- }
204
-
205
- /**
206
- *
207
- * @param {number} id
208
- * @returns {ResourceNode}
209
- */
210
- getResourceNode(id) {
211
- assert.isNonNegativeInteger(id, 'id');
212
-
213
- const resource = this.__resource_nodes[id];
214
-
215
- if (resource === undefined) {
216
- throw new Error(`Resource Node ${id} not found`);
217
- }
218
-
219
- assert.equal(resource.id, id, 'resource.id does not match');
220
-
221
- return resource;
222
- }
223
-
224
- /**
225
- * @template T
226
- * @param {number} id resource ID
227
- * @returns {T}
228
- */
229
- getDescriptor(id) {
230
- return this.getResourceEntry(id).resource_descriptor;
231
- }
232
-
233
- /**
234
- * @template T
235
- * @param {string} name
236
- * @param {T} descriptor
237
- * @returns {number}
238
- */
239
- create_resource(name, descriptor) {
240
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be created while recording, graph is ${frame_graph_state_name(this.#state)}`);
241
- assert.isString(name, 'name');
242
- assert.defined(descriptor, 'descriptor');
243
- assert.notNull(descriptor, 'descriptor');
244
- assert.isObject(descriptor, 'descriptor');
245
-
246
- const resource = this._createResourceEntry(descriptor);
247
-
248
- return this._createResourceNode(name, resource.resource_id).id;
249
- }
250
-
251
- /**
252
- * @template T
253
- * @param {T} descriptor
254
- * @return {ResourceEntry}
255
- * @private
256
- */
257
- _createResourceEntry(descriptor) {
258
- assert.defined(descriptor, 'descriptor');
259
- assert.notNull(descriptor, 'descriptor');
260
-
261
- const entry = new ResourceEntry();
262
-
263
- entry.resource_id = this.__resource_registry.length;
264
- entry.resource_descriptor = descriptor;
265
-
266
- this.__resource_registry.push(entry);
267
-
268
- return entry;
269
- }
270
-
271
- /**
272
- *
273
- * @param {string} name
274
- * @param {number} resource_id
275
- * @return {ResourceNode}
276
- * @private
277
- */
278
- _createResourceNode(name, resource_id) {
279
- assert.isString(name, 'name');
280
- assert.isNonNegativeInteger(resource_id, 'resource_id');
281
-
282
- const n = new ResourceNode();
283
-
284
- const id = this.__resource_nodes.length;
285
-
286
- n.id = id;
287
- n.name = name;
288
- n.resource_id = resource_id;
289
- n.scope = this.#scope;
290
-
291
- this.__resource_nodes[id] = n;
292
-
293
- return n;
294
- }
295
-
296
- /**
297
- *
298
- * @param {number} node_id
299
- * @returns {number} ID of the cloned resource node
300
- */
301
- clone_resource(node_id) {
302
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be cloned while recording, graph is ${frame_graph_state_name(this.#state)}`);
303
-
304
- const node = this.getResourceNode(node_id);
305
-
306
- const entry = this.__resource_registry[node.resource_id];
307
-
308
- entry.resource_version++;
309
-
310
- const clone_node = new ResourceNode();
311
- clone_node.id = this.__resource_nodes.length;
312
- clone_node.name = node.name;
313
- clone_node.resource_id = node.resource_id;
314
- clone_node.version = entry.resource_version;
315
- clone_node.scope = this.#scope;
316
-
317
- this.__resource_nodes.push(clone_node);
318
-
319
- return clone_node.id;
320
- }
321
-
322
- /**
323
- * @template T
324
- * @param {string} name
325
- * @param {ResourceDescriptor<T>} description
326
- * @param {T} resource
327
- * @returns {number}
328
- */
329
- import_resource(name, description, resource) {
330
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be imported while recording, graph is ${frame_graph_state_name(this.#state)}`);
331
-
332
- const record = this._createResourceEntry(description);
333
-
334
- record.resource = resource;
335
-
336
- record.imported = true;
337
-
338
- return this._createResourceNode(name, record.resource_id).id;
339
- }
340
-
341
- /**
342
- * @returns {boolean}
343
- * @param id
344
- */
345
- is_valid_resource(id) {
346
- const node = this.getResourceNode(id);
347
- const record = this.getResourceEntry(id);
348
-
349
- return node.version === record.resource_version;
350
- }
351
-
352
- /**
353
- * Add a new pass to the graph.
354
- * @template T
355
- * @param {string} name
356
- * @param {T} data
357
- * @param {(data: T, resources: FramePassResources, context: IFrameGraphContext) => void} execute
358
- * @returns {FramePassBuilder}
359
- */
360
- add(name, data, execute) {
361
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': passes can only be added while recording, graph is ${frame_graph_state_name(this.#state)}`);
362
- assert.isString(name, 'name');
363
-
364
- assert.defined(data, 'data');
365
- assert.notNull(data, 'data');
366
- assert.isObject(data, 'data');
367
-
368
- assert.isFunction(execute, 'execute');
369
-
370
- const pass_nodes = this.__pass_nodes;
371
-
372
- const builder = new FramePassBuilder();
373
- const node = new FramePassNode();
374
-
375
- node.id = pass_nodes.length;
376
- node.name = name;
377
- node.execute = execute;
378
- node.scope = this.#scope;
379
-
380
- pass_nodes.push(node);
381
-
382
- builder.init(this, node);
383
-
384
- node.data = data;
385
-
386
- return builder;
387
- }
388
-
389
- /**
390
- * Check the recorded graph for the two ways a read can silently return the wrong contents.
391
- *
392
- * {@link execute} runs passes in **insertion order** and never sorts, so a pass that reads a
393
- * resource written by a pass recorded after it reads whatever that resource happened to hold.
394
- * And a {@link ResourceEntry} owns exactly one concrete resource — a superseded
395
- * {@link ResourceNode} is not a snapshot of the old contents, it is the same resource holding
396
- * the new contents — so reading an out-of-date node is reading the wrong thing.
397
- *
398
- * One shape is deliberately **not** a problem: reading a transient that no pass writes is
399
- * fine, because a resource can be created by a pass that only reserves it, and the reader is
400
- * then the pass about to fill it.
401
- *
402
- * Accumulating into a shared target is **not** an exemption. {@link FramePassBuilder.write}
403
- * records a read of the handle it replaces, so a pass re-attaching a target it goes on to write
404
- * is a reader of that target, and it has to re-attach the handle its predecessor produced
405
- * rather than the one it was originally handed. Several passes rasterizing into one render
406
- * target do exactly this, and each of them threads the handle forward.
407
- *
408
- * Describes what was recorded rather than what survives culling, so it runs before culling is
409
- * computed — {@link compile} calls it.
410
- *
411
- * @param {function(string):*} [problem_consumer] invoked once per problem found
412
- * @returns {boolean} whether the graph is free of problems
413
- */
414
- validate(problem_consumer = noop) {
415
-
416
- const pass_nodes = this.__pass_nodes;
417
- const pass_node_count = pass_nodes.length;
418
-
419
- const resource_nodes = this.__resource_nodes;
420
-
421
- let valid = true;
422
-
423
- /**
424
- * Highest resource node version of a given resource entry written so far, by entry ID.
425
- * @type {Int32Array}
426
- */
427
- const current_version = new Int32Array(this.__resource_registry.length);
428
-
429
- /**
430
- * Index of the pass that wrote {@link current_version}, by resource entry ID, or -1.
431
- * @type {Int32Array}
432
- */
433
- const current_version_writer = new Int32Array(this.__resource_registry.length).fill(-1);
434
-
435
- for (let i = 0; i < pass_node_count; i++) {
436
- const pass = pass_nodes[i];
437
-
438
- for (const id of pass.resource_reads) {
439
- const node = resource_nodes[id];
440
-
441
- const producer = node.producer;
442
-
443
- if (producer !== null && producer.id > i) {
444
- problem_consumer(`Pass ${i} '${pass.name}' reads '${node.name}', which is written by pass ${producer.id} '${producer.name}'. Passes execute in the order they were added, so the read happens before the write.`);
445
-
446
- valid = false;
447
- }
448
-
449
- const version = current_version[node.resource_id];
450
-
451
- if (node.version < version) {
452
- const writer_index = current_version_writer[node.resource_id];
453
-
454
- problem_consumer(`Pass ${i} '${pass.name}' reads version ${node.version} of '${node.name}', which pass ${writer_index} '${pass_nodes[writer_index].name}' has already superseded with version ${version}. Both versions name one resource, so the read returns the newer contents.`);
455
-
456
- valid = false;
457
- }
458
- }
459
-
460
- for (const id of pass.resource_writes) {
461
- const node = resource_nodes[id];
462
-
463
- if (node.version > current_version[node.resource_id]) {
464
- current_version[node.resource_id] = node.version;
465
- current_version_writer[node.resource_id] = i;
466
- }
467
- }
468
- }
469
-
470
- return valid;
471
- }
472
-
473
- compile() {
474
- assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': compile() is one-shot, a second compile accumulates resource reference counts instead of resetting them and culling stops working, graph is ${frame_graph_state_name(this.#state)}`);
475
-
476
- assert.equal(this.#scope, null, `FrameGraph '${this.name}': recording finished with scope '${this.#scope}' still open, every push_scope() needs its pop_scope()`);
477
- assert.ok(this.validate(console.error), `FrameGraph '${this.name}': the recorded graph is invalid, the problems are logged above`);
478
-
479
- this.#state = FrameGraphState.Compiled;
480
-
481
- const pass_nodes = this.__pass_nodes;
482
-
483
- // wire resources of passes
484
- const pass_node_count = pass_nodes.length;
485
- const resource_nodes = this.__resource_nodes;
486
-
487
- for (let i = 0; i < pass_node_count; i++) {
488
- const pass = pass_nodes[i];
489
-
490
- // mask pass as having number of references equal to number of resources it writes to
491
- pass.ref_count = pass.resource_writes.length;
492
-
493
- for (const id of pass.resource_reads) {
494
- const read_node = resource_nodes[id];
495
-
496
- read_node.ref_count++;
497
- }
498
- }
499
-
500
- // perform culling
501
- /**
502
- *
503
- * @type {ResourceNode[]}
504
- */
505
- const unreferenced_resources = [];
506
-
507
- for (const node of resource_nodes) {
508
- if (node.ref_count === 0) {
509
- unreferenced_resources.push(node);
510
- }
511
- }
512
-
513
- while (unreferenced_resources.length > 0) {
514
- const unreferenced_resource = unreferenced_resources.pop();
515
-
516
- const producer = unreferenced_resource.producer;
517
-
518
- if (producer === null || producer.has_side_effects) {
519
- continue;
520
- }
521
-
522
- assert.greaterThanOrEqual(producer.ref_count, 1, 'must have at least one reference'); // Why?
523
-
524
- // disconnect unreferenced resource from producer
525
- producer.ref_count--;
526
-
527
- if (producer.ref_count === 0) {
528
-
529
- // producer is no longer referenced
530
-
531
- for (const id of producer.resource_reads) {
532
- const node = resource_nodes[id];
533
-
534
- // disconnect producer from all its reads (upstream)
535
- node.ref_count--;
536
-
537
- if (node.ref_count === 0) {
538
- // resource is no longer referenced
539
- unreferenced_resources.push(node);
540
- }
541
- }
542
- }
543
- }
544
-
545
- // calculate lifetimes
546
- for (let i = 0; i < pass_node_count; i++) {
547
- const pass = pass_nodes[i];
548
- if (pass.ref_count === 0) {
549
- // unused pass
550
- continue;
551
- }
552
-
553
- for (const id of pass.resource_creates) {
554
- const entry = this.getResourceEntry(id);
555
- entry.producer = pass;
556
- entry.last = pass;
557
- }
558
-
559
- for (const id of pass.resource_writes) {
560
- this.getResourceEntry(id).last = pass;
561
- }
562
-
563
- for (const id of pass.resource_reads) {
564
- this.getResourceEntry(id).last = pass;
565
- }
566
- }
567
- }
568
-
569
- /**
570
- *
571
- * @param {IFrameGraphContext} context
572
- */
573
- execute(context) {
574
- assert.equal(this.#state, FrameGraphState.Compiled, `FrameGraph '${this.name}': execute() requires a compiled graph, an uncompiled one has every reference count at 0 and culls itself silently, graph is ${frame_graph_state_name(this.#state)}`);
575
- assert.defined(context, 'context');
576
- assert.notNull(context, 'context');
577
- assert.isObject(context, 'context');
578
-
579
- this.#state = FrameGraphState.Executed;
580
-
581
- const pass_nodes = this.__pass_nodes;
582
- const pass_node_count = pass_nodes.length;
583
-
584
- for (let i = 0; i < pass_node_count; i++) {
585
- const node = pass_nodes[i];
586
-
587
- if (!node.can_execute()) {
588
- continue;
589
- }
590
-
591
- const node_created_resources = node.resource_creates;
592
-
593
- for (const id of node_created_resources) {
594
-
595
- // allocate resource
596
- this.getResourceEntry(id).create(context.resource_manager);
597
- }
598
-
599
- const resources = new FramePassResources();
600
- resources.init(this, node);
601
-
602
- // execute pass
603
- try {
604
- node.execute(node.data, resources, context);
605
- } catch (e) {
606
- // decorate
607
- const error = new Error(`RenderPass '${node.name}' failed to execute`);
608
-
609
- error.cause = e;
610
-
611
- throw error;
612
- }
613
-
614
-
615
- const resource_entries = this.__resource_registry;
616
- const resource_entry_count = resource_entries.length;
617
-
618
- for (let entry_index = 0; entry_index < resource_entry_count; entry_index++) {
619
- const entry = resource_entries[entry_index];
620
-
621
- if (entry.last === node && entry.isTransient()) {
622
- // this was the last user of the resource and the resource is transient (no external usage)
623
- entry.destroy(context.resource_manager);
624
- }
625
-
626
- }
627
-
628
- }
629
-
630
- this.onExecuted.send2(context, this);
631
- }
632
-
633
- /**
634
- * Should only call after {@link compile}
635
- *
636
- * One entry in `resources` per resource **node**, that is per version, keyed by node ID. A pass
637
- * records the node it read or wrote and not the underlying resource, so this is the id space
638
- * `passes[].reads` and `passes[].writes` point into; a registry-keyed export would leave those
639
- * references dangling on every version past the first.
640
- *
641
- * Can be visualized with this tool: https://skaarj1989.github.io/FrameGraph/
642
- *
643
- * @see https://github.com/skaarj1989/FrameGraph/blob/viewer/JsonWriter.hpp
644
- * @return {{passes: [], resources: []}}
645
- */
646
- exportToJson() {
647
-
648
- const resources = this.__resource_nodes.map(node => {
649
- const entry = this.__resource_registry[node.resource_id];
650
-
651
- const j = {
652
- id: node.id,
653
- name: node.name,
654
- transient: entry.isTransient(),
655
- };
656
-
657
- if (node.version > 0) {
658
- j.version = node.version;
659
- }
660
-
661
- const description = entry.resource_descriptor?.toString();
662
-
663
- if (description) {
664
- j.description = description;
665
- }
666
-
667
- if (node.producer !== null) {
668
- j.createdBy = node.producer.id;
669
- }
670
-
671
- const readers = [];
672
- const writers = [];
673
-
674
- for (const pass of this.__pass_nodes) {
675
- if (pass.reads(node.id)) {
676
- readers.push(pass.id);
677
- }
678
- if (pass.writes(node.id)) {
679
- writers.push(pass.id);
680
- }
681
- }
682
-
683
- if (readers.length > 0) {
684
- j.readers = readers;
685
- }
686
- if (writers.length > 0) {
687
- j.writers = writers;
688
- }
689
-
690
- return j;
691
- });
692
-
693
- return {
694
- passes: this.__pass_nodes.map(node => {
695
- return {
696
- id: node.id,
697
- name: node.name,
698
- culled: !node.can_execute(),
699
- reads: node.resource_reads,
700
- writes: node.resource_writes,
701
- }
702
- }),
703
- resources
704
- }
705
- }
706
-
707
- /**
708
- * Export the graph diagram in GraphViz DOT format.
709
- * Useful for debugging.
710
- * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
711
- * @return {string}
712
- */
713
- exportToDot() {
714
- const out = new LineBuilder();
715
-
716
- out.add("digraph FrameGraph {");
717
- out.indent();
718
- out.add("graph [style=invis, rankdir=\"TB\" ordering=out, splines=spline]");
719
- out.add("node [shape=record, fontname=\"helvetica\", fontsize=10, margin=\"0.2,0.03\"]");
720
-
721
-
722
- // -- Define pass nodes
723
-
724
- /**
725
- * @param {FramePassNode} node
726
- * @returns {string}
727
- */
728
- const pass_declaration = (node) => `P${node.id} [label=<{ {<B>${node.name}</B>} | {${(node.has_side_effects ? "&#x2605; " : "")} Refs: ${node.ref_count}<BR/> Index: ${node.id}} }> style=\"rounded,filled\", fillcolor=${(node.ref_count > 0 || node.has_side_effects) ? "orange" : "lightgray"}]`;
729
-
730
- out.add("");
731
- out.add("# Pass Nodes");
732
- for (const node of this.__pass_nodes) {
733
- if (node.scope === null) {
734
- out.add(pass_declaration(node));
735
- }
736
- }
737
-
738
- // -- Define resource nodes
739
-
740
- /**
741
- *
742
- * @param {string} type
743
- * @returns {number}
744
- */
745
- function type_to_color(type) {
746
-
747
- const hash_32 = computeStringHash(type);
748
-
749
- const hash_16 = (hash_32 & 0xffff) ^ (hash_32 >>> 16);
750
-
751
- return hash_16 / 0xffff;
752
- }
753
-
754
- /**
755
- *
756
- * @param {ResourceEntry} entry
757
- * @return {string}
758
- */
759
- function entry_to_background_color(entry) {
760
-
761
- let color_hue = type_to_color(entry.resource_descriptor?.type ?? "");
762
-
763
- // remap hue away from red
764
- color_hue = lerp(0.10, 0.90, color_hue);
765
-
766
- const saturation = entry.isImported() ? 0.2 : 0.07;
767
-
768
- const color_rgb = [];
769
- okhsv_to_linear_srgb(color_rgb, color_hue, saturation, 1)
770
-
771
- const hex = rgb2hex(...color_rgb.map(float_to_uint8));
772
-
773
- return `#${hex}`;
774
- }
775
-
776
- /**
777
- * @param {ResourceNode} node
778
- * @returns {string}
779
- */
780
- const resource_declaration = (node) => {
781
- const entry = this.__resource_registry[node.resource_id];
782
-
783
- const type = graphviz_escape_string(entry.resource_descriptor?.type ?? "");
784
-
785
- const block_title = `${entry.isImported() ? "↪ " : ""}${node.name}`;
786
- const block_tooltip = graphviz_escape_string(entry.toString());
787
-
788
- return `R${entry.resource_id}_${node.version} [label=<{ {<B>${block_title}</B>${node.version > 0 ? ` <FONT>v${(node.version + 1)}</FONT>` : ""}<BR/>${type}} | {Index: ${entry.resource_id}<BR/> Refs : ${node.ref_count} } }> style=filled, fillcolor="${entry_to_background_color(entry)}" tooltip="${block_tooltip}"]`;
789
- };
790
-
791
- out.add("");
792
- out.add("# Resource Nodes");
793
- for (const node of this.__resource_nodes) {
794
- if (node.scope === null) {
795
- out.add(resource_declaration(node));
796
- }
797
- }
798
-
799
- // -- Define scoped nodes, inside the cluster of the scope that recorded them
800
-
801
- /**
802
- * Scopes that stamped at least one node, and the scopes enclosing those.
803
- * @type {Set<FrameGraphScope>}
804
- */
805
- const scopes = new Set();
806
-
807
- for (const node of [...this.__pass_nodes, ...this.__resource_nodes]) {
808
- for (let scope = node.scope; scope !== null && !scopes.has(scope); scope = scope.parent) {
809
- scopes.add(scope);
810
- }
811
- }
812
-
813
- /**
814
- * @param {FrameGraphScope} scope
815
- */
816
- const emit_scope = (scope) => {
817
- out.add(`subgraph cluster_${scope.id} {`);
818
- out.indent();
819
-
820
- // the root graph is drawn invisible, so a cluster has to ask for its own frame
821
- out.add(`label="${graphviz_escape_string(scope.name)}"`);
822
- out.add("style=\"rounded,dashed\", color=\"gray55\", fontname=\"helvetica\", fontsize=10, labeljust=\"l\"");
823
-
824
- for (const node of this.__pass_nodes) {
825
- if (node.scope === scope) {
826
- out.add(pass_declaration(node));
827
- }
828
- }
829
-
830
- for (const node of this.__resource_nodes) {
831
- if (node.scope === scope) {
832
- out.add(resource_declaration(node));
833
- }
834
- }
835
-
836
- for (const child of scopes) {
837
- if (child.parent === scope) {
838
- emit_scope(child);
839
- }
840
- }
841
-
842
- out.dedent();
843
- out.add("}");
844
- };
845
-
846
- if (scopes.size > 0) {
847
- out.add("");
848
- out.add("# Scopes");
849
-
850
- for (const scope of scopes) {
851
- if (scope.parent === null) {
852
- emit_scope(scope);
853
- }
854
- }
855
- }
856
-
857
- // -- Each pass node points to the resource that it writes
858
-
859
- out.add("");
860
- out.add("# Resource Writes");
861
- for (const node of this.__pass_nodes) {
862
- out.add(`P${node.id} -> {`);
863
- out.indent();
864
- for (const id of node.resource_writes) {
865
- const written = this.__resource_nodes[id];
866
- out.add(`R${written.resource_id}_${written.version} `);
867
- }
868
- out.dedent();
869
- out.add("} [color=orangered]");
870
- }
871
-
872
- // -- Each resource node points to the pass where it's consumed
873
-
874
- out.add("");
875
- out.add("# Resource Reads");
876
-
877
- for (const node of this.__resource_nodes) {
878
- out.add(`R${node.resource_id}_${node.version} -> {`);
879
- out.indent();
880
- // find all readers of this resource node
881
- for (const pass of this.__pass_nodes) {
882
- for (const id of pass.resource_reads)
883
- if (id === node.id) {
884
- out.add(`P${pass.id} `);
885
- }
886
- }
887
- out.dedent();
888
- out.add("} [color=olivedrab3]");
889
- }
890
- out.dedent();
891
- out.add("}");
892
- return out.build();
893
- }
894
- }
895
-
896
- /**
897
- * Type check shortcut.
898
- * @readonly
899
- * @type {boolean}
900
- */
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { float_to_uint8 } from "../../../../core/binary/float_to_uint8.js";
3
+ import LineBuilder from "../../../../core/codegen/LineBuilder.js";
4
+ import { rgb_to_hex } from "../../../../core/color/hex/rgb_to_hex.js";
5
+ import { okhsv_to_linear_srgb } from "../../../../core/color/oklab/okhsv_to_linear_srgb.js";
6
+ import { Signal } from "../../../../core/events/signal/Signal.js";
7
+ import { noop } from "../../../../core/function/noop.js";
8
+ import { graphviz_escape_string } from "../../../../core/graph/format/graphviz/graphviz_escape_string.js";
9
+ import { lerp } from "../../../../core/math/lerp.js";
10
+ import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
11
+ import { FrameGraphScope } from "./FrameGraphScope.js";
12
+ import { FrameGraphState, frame_graph_state_name } from "./FrameGraphState.js";
13
+ import { FramePassBuilder } from "./FramePassBuilder.js";
14
+ import { FramePassNode } from "./FramePassNode.js";
15
+ import { FramePassResources } from "./FramePassResources.js";
16
+ import { ResourceEntry } from "./ResourceEntry.js";
17
+ import { ResourceNode } from "./ResourceNode.js";
18
+
19
+ /**
20
+ * A high-level rendering architecture system that manages render passes and their resource dependencies.
21
+ * This class schedules frame execution by building a directed graph of resources (textures, buffers) and the passes that read or write to them.
22
+ *
23
+ * By deferring execution until after the graph is compiled, it automatically handles complex tasks such as:
24
+ * - Culling unreferenced resources and passes (dead code elimination for rendering).
25
+ * - Automatic resource lifetime management (allocating transient resources just-in-time and destroying them immediately after their last use).
26
+ * - Visualizing the rendering pipeline via GraphViz DOT exports.
27
+ *
28
+ * The graph is **one-shot**: it is recorded into, then compiled, then executed, and it never goes
29
+ * back. {@link compile} derives reference counts and resource lifetimes from what was recorded, so
30
+ * recording into a compiled graph corrupts them, and compiling twice accumulates resource reference
31
+ * counts instead of resetting them, which disables culling entirely. Build a new graph per frame.
32
+ *
33
+ * @see FrameGraphState
34
+ *
35
+ * @source Based on the Frostbite's GDC paper "FrameGraph: Extensible Rendering Architecture in Frostbite" by Yuriy O'Donnell
36
+ * @example
37
+ * const graph = new FrameGraph("My Graph");
38
+ *
39
+ * const pass_data = {};
40
+ * const pass = graph.add("GBuffer Pass", pass_data, (data, resources, context) => {
41
+ *
42
+ * context.beginRenderPass({
43
+ * depth_attachment: resources.get(data.depth),
44
+ * color_attachments:[
45
+ * resources.get(data.normal),
46
+ * resources.get(data.albedo),
47
+ * ]
48
+ * });
49
+ *
50
+ * for (const renderable in renderables){
51
+ * drawMesh(context, renderable.mesh, renderable.material);
52
+ * }
53
+ *
54
+ * context.endRenderPass();
55
+ *
56
+ * });
57
+ *
58
+ * pass_data.depth = pass.create("depth", {...});
59
+ * pass_data.albedo = pass.create("albedo", {...});
60
+ * pass_data.normal = pass.create("normal", {...});
61
+ *
62
+ * graph.compile();
63
+ * graph.execute(context);
64
+ *
65
+ */
66
+ export class FrameGraph {
67
+ /**
68
+ * Human-readable name, used for debugging and UI primarily.
69
+ * There is no uniqueness guarantee
70
+ * @type {string}
71
+ */
72
+ name = "";
73
+
74
+ /**
75
+ *
76
+ * @type {Signal<IFrameGraphContext,this>}
77
+ */
78
+ onExecuted = new Signal();
79
+
80
+ /**
81
+ * A pass is about to run, and everything encoded until {@link onPassEnd} belongs to it.
82
+ *
83
+ * This exists because **a GPU pass label is not a key back to the pass that opened it.** One
84
+ * frame graph pass routinely opens several GPU passes under labels of their own choosing —
85
+ * `graph_build_hzb` records one node and `hzb.build` opens a compute pass per mip — so a
86
+ * profiler holding a pile of timestamped labels has no way to work out afterwards which node
87
+ * produced them. Bracketing execution is what supplies the answer, and it cannot be recovered
88
+ * any other way.
89
+ *
90
+ * Fires only for passes that survive culling, in execution order, and never nests: {@link
91
+ * execute} runs one pass at a time.
92
+ *
93
+ * @type {Signal<FramePassNode,this>}
94
+ */
95
+ onPassBegin = new Signal();
96
+
97
+ /**
98
+ * The pass most recently announced by {@link onPassBegin} has finished encoding.
99
+ *
100
+ * Sent even when the pass threw, before the failure is re-raised, so a listener that opened
101
+ * something on begin is not left holding it open by an exception. A listener must therefore
102
+ * treat this as "that pass is over" rather than "that pass succeeded".
103
+ *
104
+ * @type {Signal<FramePassNode,this>}
105
+ */
106
+ onPassEnd = new Signal();
107
+
108
+ /**
109
+ *
110
+ * @type {FramePassNode[]}
111
+ * @private
112
+ */
113
+ __pass_nodes = [];
114
+
115
+ /**
116
+ *
117
+ * @type {ResourceNode[]}
118
+ * @private
119
+ */
120
+ __resource_nodes = [];
121
+
122
+ /**
123
+ *
124
+ * @type {ResourceEntry[]}
125
+ * @private
126
+ */
127
+ __resource_registry = [];
128
+
129
+ /**
130
+ * Position in the one-shot lifecycle.
131
+ * @type {FrameGraphState}
132
+ */
133
+ #state = FrameGraphState.Recording;
134
+
135
+ /**
136
+ * Innermost open recording scope, or null. The enclosing scopes are its
137
+ * {@link FrameGraphScope.parent} chain, so this one field is the whole stack.
138
+ * @type {FrameGraphScope|null}
139
+ */
140
+ #scope = null;
141
+
142
+ /**
143
+ * Scopes opened so far, which is what gives each one an ID unique within this graph.
144
+ * @type {number}
145
+ */
146
+ #scope_count = 0;
147
+
148
+ /**
149
+ *
150
+ * @param {string} name
151
+ */
152
+ constructor(name = "") {
153
+ assert.isString(name, 'name');
154
+
155
+
156
+ this.name = name;
157
+ }
158
+
159
+ /**
160
+ * Position in the one-shot lifecycle. Recording is only legal in
161
+ * {@link FrameGraphState.Recording}, and {@link execute} is only legal in
162
+ * {@link FrameGraphState.Compiled}.
163
+ * @returns {FrameGraphState}
164
+ */
165
+ get state() {
166
+ return this.#state;
167
+ }
168
+
169
+ /**
170
+ * Innermost open recording scope, or null when recording outside of one.
171
+ * @returns {FrameGraphScope|null}
172
+ */
173
+ get scope() {
174
+ return this.#scope;
175
+ }
176
+
177
+ /**
178
+ * Open a recording scope. Every pass and resource node created until the matching
179
+ * {@link pop_scope} is stamped with it, nested scopes included.
180
+ *
181
+ * This is how the graph attributes work to whoever recorded it. Node names come from the
182
+ * helpers that happened to build them and say nothing about the caller, so without a scope a
183
+ * DOT export cannot group a frame by the thing each group belongs to, and a diagnostic cannot
184
+ * ask whether everything one caller recorded was culled.
185
+ *
186
+ * @param {string} name
187
+ * @returns {FrameGraphScope} the scope opened, for later queries against node stamps
188
+ */
189
+ push_scope(name) {
190
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': scopes can only be opened while recording, graph is ${frame_graph_state_name(this.#state)}`);
191
+ assert.isString(name, 'name');
192
+
193
+ const scope = new FrameGraphScope();
194
+
195
+ scope.name = name;
196
+ scope.id = this.#scope_count++;
197
+ scope.parent = this.#scope;
198
+
199
+ this.#scope = scope;
200
+
201
+ return scope;
202
+ }
203
+
204
+ /**
205
+ * Close the innermost open recording scope, returning to the one that encloses it.
206
+ */
207
+ pop_scope() {
208
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': scopes can only be closed while recording, graph is ${frame_graph_state_name(this.#state)}`);
209
+ assert.notNull(this.#scope, `FrameGraph '${this.name}': pop_scope() without a scope open`);
210
+
211
+ this.#scope = this.#scope.parent;
212
+ }
213
+
214
+ /**
215
+ *
216
+ * @param {number} id Resource Node ID
217
+ * @returns {ResourceEntry}
218
+ */
219
+ getResourceEntry(id) {
220
+ const node = this.getResourceNode(id);
221
+
222
+ assert.defined(node, 'node');
223
+
224
+ const registry = this.__resource_registry;
225
+
226
+ const entry = registry[node.resource_id];
227
+
228
+ assert.defined(entry, 'entry');
229
+
230
+ return entry;
231
+ }
232
+
233
+ /**
234
+ *
235
+ * @param {number} id
236
+ * @returns {ResourceNode}
237
+ */
238
+ getResourceNode(id) {
239
+ assert.isNonNegativeInteger(id, 'id');
240
+
241
+ const resource = this.__resource_nodes[id];
242
+
243
+ if (resource === undefined) {
244
+ throw new Error(`Resource Node ${id} not found`);
245
+ }
246
+
247
+ assert.equal(resource.id, id, 'resource.id does not match');
248
+
249
+ return resource;
250
+ }
251
+
252
+ /**
253
+ * @template T
254
+ * @param {number} id resource ID
255
+ * @returns {T}
256
+ */
257
+ getDescriptor(id) {
258
+ return this.getResourceEntry(id).resource_descriptor;
259
+ }
260
+
261
+ /**
262
+ * @template T
263
+ * @param {string} name
264
+ * @param {T} descriptor
265
+ * @returns {number}
266
+ */
267
+ create_resource(name, descriptor) {
268
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be created while recording, graph is ${frame_graph_state_name(this.#state)}`);
269
+ assert.isString(name, 'name');
270
+ assert.defined(descriptor, 'descriptor');
271
+ assert.notNull(descriptor, 'descriptor');
272
+ assert.isObject(descriptor, 'descriptor');
273
+
274
+ const resource = this._createResourceEntry(descriptor);
275
+
276
+ return this._createResourceNode(name, resource.resource_id).id;
277
+ }
278
+
279
+ /**
280
+ * @template T
281
+ * @param {T} descriptor
282
+ * @return {ResourceEntry}
283
+ * @private
284
+ */
285
+ _createResourceEntry(descriptor) {
286
+ assert.defined(descriptor, 'descriptor');
287
+ assert.notNull(descriptor, 'descriptor');
288
+
289
+ const entry = new ResourceEntry();
290
+
291
+ entry.resource_id = this.__resource_registry.length;
292
+ entry.resource_descriptor = descriptor;
293
+
294
+ this.__resource_registry.push(entry);
295
+
296
+ return entry;
297
+ }
298
+
299
+ /**
300
+ *
301
+ * @param {string} name
302
+ * @param {number} resource_id
303
+ * @return {ResourceNode}
304
+ * @private
305
+ */
306
+ _createResourceNode(name, resource_id) {
307
+ assert.isString(name, 'name');
308
+ assert.isNonNegativeInteger(resource_id, 'resource_id');
309
+
310
+ const n = new ResourceNode();
311
+
312
+ const id = this.__resource_nodes.length;
313
+
314
+ n.id = id;
315
+ n.name = name;
316
+ n.resource_id = resource_id;
317
+ n.scope = this.#scope;
318
+
319
+ this.__resource_nodes[id] = n;
320
+
321
+ return n;
322
+ }
323
+
324
+ /**
325
+ *
326
+ * @param {number} node_id
327
+ * @returns {number} ID of the cloned resource node
328
+ */
329
+ clone_resource(node_id) {
330
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be cloned while recording, graph is ${frame_graph_state_name(this.#state)}`);
331
+
332
+ const node = this.getResourceNode(node_id);
333
+
334
+ const entry = this.__resource_registry[node.resource_id];
335
+
336
+ entry.resource_version++;
337
+
338
+ const clone_node = new ResourceNode();
339
+ clone_node.id = this.__resource_nodes.length;
340
+ clone_node.name = node.name;
341
+ clone_node.resource_id = node.resource_id;
342
+ clone_node.version = entry.resource_version;
343
+ clone_node.scope = this.#scope;
344
+
345
+ this.__resource_nodes.push(clone_node);
346
+
347
+ return clone_node.id;
348
+ }
349
+
350
+ /**
351
+ * @template T
352
+ * @param {string} name
353
+ * @param {ResourceDescriptor<T>} description
354
+ * @param {T} resource
355
+ * @returns {number}
356
+ */
357
+ import_resource(name, description, resource) {
358
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': resources can only be imported while recording, graph is ${frame_graph_state_name(this.#state)}`);
359
+
360
+ const record = this._createResourceEntry(description);
361
+
362
+ record.resource = resource;
363
+
364
+ record.imported = true;
365
+
366
+ return this._createResourceNode(name, record.resource_id).id;
367
+ }
368
+
369
+ /**
370
+ * @returns {boolean}
371
+ * @param id
372
+ */
373
+ is_valid_resource(id) {
374
+ const node = this.getResourceNode(id);
375
+ const record = this.getResourceEntry(id);
376
+
377
+ return node.version === record.resource_version;
378
+ }
379
+
380
+ /**
381
+ * Add a new pass to the graph.
382
+ * @template T
383
+ * @param {string} name
384
+ * @param {T} data
385
+ * @param {(data: T, resources: FramePassResources, context: IFrameGraphContext) => void} execute
386
+ * @returns {FramePassBuilder}
387
+ */
388
+ add(name, data, execute) {
389
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': passes can only be added while recording, graph is ${frame_graph_state_name(this.#state)}`);
390
+ assert.isString(name, 'name');
391
+
392
+ assert.defined(data, 'data');
393
+ assert.notNull(data, 'data');
394
+ assert.isObject(data, 'data');
395
+
396
+ assert.isFunction(execute, 'execute');
397
+
398
+ const pass_nodes = this.__pass_nodes;
399
+
400
+ const builder = new FramePassBuilder();
401
+ const node = new FramePassNode();
402
+
403
+ node.id = pass_nodes.length;
404
+ node.name = name;
405
+ node.execute = execute;
406
+ node.scope = this.#scope;
407
+
408
+ pass_nodes.push(node);
409
+
410
+ builder.init(this, node);
411
+
412
+ node.data = data;
413
+
414
+ return builder;
415
+ }
416
+
417
+ /**
418
+ * Check the recorded graph for the two ways a read can silently return the wrong contents.
419
+ *
420
+ * {@link execute} runs passes in **insertion order** and never sorts, so a pass that reads a
421
+ * resource written by a pass recorded after it reads whatever that resource happened to hold.
422
+ * And a {@link ResourceEntry} owns exactly one concrete resource — a superseded
423
+ * {@link ResourceNode} is not a snapshot of the old contents, it is the same resource holding
424
+ * the new contents so reading an out-of-date node is reading the wrong thing.
425
+ *
426
+ * One shape is deliberately **not** a problem: reading a transient that no pass writes is
427
+ * fine, because a resource can be created by a pass that only reserves it, and the reader is
428
+ * then the pass about to fill it.
429
+ *
430
+ * Accumulating into a shared target is **not** an exemption. {@link FramePassBuilder.write}
431
+ * records a read of the handle it replaces, so a pass re-attaching a target it goes on to write
432
+ * is a reader of that target, and it has to re-attach the handle its predecessor produced
433
+ * rather than the one it was originally handed. Several passes rasterizing into one render
434
+ * target do exactly this, and each of them threads the handle forward.
435
+ *
436
+ * Describes what was recorded rather than what survives culling, so it runs before culling is
437
+ * computed — {@link compile} calls it.
438
+ *
439
+ * @param {function(string):*} [problem_consumer] invoked once per problem found
440
+ * @returns {boolean} whether the graph is free of problems
441
+ */
442
+ validate(problem_consumer = noop) {
443
+
444
+ const pass_nodes = this.__pass_nodes;
445
+ const pass_node_count = pass_nodes.length;
446
+
447
+ const resource_nodes = this.__resource_nodes;
448
+
449
+ let valid = true;
450
+
451
+ /**
452
+ * Highest resource node version of a given resource entry written so far, by entry ID.
453
+ * @type {Int32Array}
454
+ */
455
+ const current_version = new Int32Array(this.__resource_registry.length);
456
+
457
+ /**
458
+ * Index of the pass that wrote {@link current_version}, by resource entry ID, or -1.
459
+ * @type {Int32Array}
460
+ */
461
+ const current_version_writer = new Int32Array(this.__resource_registry.length).fill(-1);
462
+
463
+ for (let i = 0; i < pass_node_count; i++) {
464
+ const pass = pass_nodes[i];
465
+
466
+ for (const id of pass.resource_reads) {
467
+ const node = resource_nodes[id];
468
+
469
+ const producer = node.producer;
470
+
471
+ if (producer !== null && producer.id > i) {
472
+ problem_consumer(`Pass ${i} '${pass.name}' reads '${node.name}', which is written by pass ${producer.id} '${producer.name}'. Passes execute in the order they were added, so the read happens before the write.`);
473
+
474
+ valid = false;
475
+ }
476
+
477
+ const version = current_version[node.resource_id];
478
+
479
+ if (node.version < version) {
480
+ const writer_index = current_version_writer[node.resource_id];
481
+
482
+ problem_consumer(`Pass ${i} '${pass.name}' reads version ${node.version} of '${node.name}', which pass ${writer_index} '${pass_nodes[writer_index].name}' has already superseded with version ${version}. Both versions name one resource, so the read returns the newer contents.`);
483
+
484
+ valid = false;
485
+ }
486
+ }
487
+
488
+ for (const id of pass.resource_writes) {
489
+ const node = resource_nodes[id];
490
+
491
+ if (node.version > current_version[node.resource_id]) {
492
+ current_version[node.resource_id] = node.version;
493
+ current_version_writer[node.resource_id] = i;
494
+ }
495
+ }
496
+ }
497
+
498
+ return valid;
499
+ }
500
+
501
+ compile() {
502
+ assert.equal(this.#state, FrameGraphState.Recording, `FrameGraph '${this.name}': compile() is one-shot, a second compile accumulates resource reference counts instead of resetting them and culling stops working, graph is ${frame_graph_state_name(this.#state)}`);
503
+
504
+ assert.equal(this.#scope, null, `FrameGraph '${this.name}': recording finished with scope '${this.#scope}' still open, every push_scope() needs its pop_scope()`);
505
+ assert.ok(this.validate(console.error), `FrameGraph '${this.name}': the recorded graph is invalid, the problems are logged above`);
506
+
507
+ this.#state = FrameGraphState.Compiled;
508
+
509
+ const pass_nodes = this.__pass_nodes;
510
+
511
+ // wire resources of passes
512
+ const pass_node_count = pass_nodes.length;
513
+ const resource_nodes = this.__resource_nodes;
514
+
515
+ for (let i = 0; i < pass_node_count; i++) {
516
+ const pass = pass_nodes[i];
517
+
518
+ // mask pass as having number of references equal to number of resources it writes to
519
+ pass.ref_count = pass.resource_writes.length;
520
+
521
+ for (const id of pass.resource_reads) {
522
+ const read_node = resource_nodes[id];
523
+
524
+ read_node.ref_count++;
525
+ }
526
+ }
527
+
528
+ // perform culling
529
+ /**
530
+ *
531
+ * @type {ResourceNode[]}
532
+ */
533
+ const unreferenced_resources = [];
534
+
535
+ for (const node of resource_nodes) {
536
+ if (node.ref_count === 0) {
537
+ unreferenced_resources.push(node);
538
+ }
539
+ }
540
+
541
+ while (unreferenced_resources.length > 0) {
542
+ const unreferenced_resource = unreferenced_resources.pop();
543
+
544
+ const producer = unreferenced_resource.producer;
545
+
546
+ if (producer === null || producer.has_side_effects) {
547
+ continue;
548
+ }
549
+
550
+ assert.greaterThanOrEqual(producer.ref_count, 1, 'must have at least one reference'); // Why?
551
+
552
+ // disconnect unreferenced resource from producer
553
+ producer.ref_count--;
554
+
555
+ if (producer.ref_count === 0) {
556
+
557
+ // producer is no longer referenced
558
+
559
+ for (const id of producer.resource_reads) {
560
+ const node = resource_nodes[id];
561
+
562
+ // disconnect producer from all its reads (upstream)
563
+ node.ref_count--;
564
+
565
+ if (node.ref_count === 0) {
566
+ // resource is no longer referenced
567
+ unreferenced_resources.push(node);
568
+ }
569
+ }
570
+ }
571
+ }
572
+
573
+ // calculate lifetimes
574
+ for (let i = 0; i < pass_node_count; i++) {
575
+ const pass = pass_nodes[i];
576
+ if (pass.ref_count === 0) {
577
+ // unused pass
578
+ continue;
579
+ }
580
+
581
+ for (const id of pass.resource_creates) {
582
+ const entry = this.getResourceEntry(id);
583
+ entry.producer = pass;
584
+ entry.last = pass;
585
+ }
586
+
587
+ for (const id of pass.resource_writes) {
588
+ this.getResourceEntry(id).last = pass;
589
+ }
590
+
591
+ for (const id of pass.resource_reads) {
592
+ this.getResourceEntry(id).last = pass;
593
+ }
594
+ }
595
+ }
596
+
597
+ /**
598
+ *
599
+ * @param {IFrameGraphContext} context
600
+ */
601
+ execute(context) {
602
+ assert.equal(this.#state, FrameGraphState.Compiled, `FrameGraph '${this.name}': execute() requires a compiled graph, an uncompiled one has every reference count at 0 and culls itself silently, graph is ${frame_graph_state_name(this.#state)}`);
603
+ assert.defined(context, 'context');
604
+ assert.notNull(context, 'context');
605
+ assert.isObject(context, 'context');
606
+
607
+ this.#state = FrameGraphState.Executed;
608
+
609
+ const pass_nodes = this.__pass_nodes;
610
+ const pass_node_count = pass_nodes.length;
611
+
612
+ for (let i = 0; i < pass_node_count; i++) {
613
+ const node = pass_nodes[i];
614
+
615
+ if (!node.can_execute()) {
616
+ continue;
617
+ }
618
+
619
+ const node_created_resources = node.resource_creates;
620
+
621
+ for (const id of node_created_resources) {
622
+
623
+ // allocate resource
624
+ this.getResourceEntry(id).create(context.resource_manager);
625
+ }
626
+
627
+ const resources = new FramePassResources();
628
+ resources.init(this, node);
629
+
630
+ /*
631
+ Guarded rather than dispatched unconditionally: `send2` on a signal with no handlers
632
+ still bumps `generation` and walks a Map iterator, and this pair runs twice per pass
633
+ over a couple of hundred passes a frame. Nothing observes the graph in a normal frame,
634
+ so the common case should cost a size comparison and nothing else.
635
+ */
636
+ const announce_passes = this.onPassBegin.hasHandlers() || this.onPassEnd.hasHandlers();
637
+
638
+ if (announce_passes) {
639
+ this.onPassBegin.send2(node, this);
640
+ }
641
+
642
+ // execute pass
643
+ try {
644
+ node.execute(node.data, resources, context);
645
+ } catch (e) {
646
+ // decorate
647
+ const error = new Error(`RenderPass '${node.name}' failed to execute`);
648
+
649
+ error.cause = e;
650
+
651
+ throw error;
652
+ } finally {
653
+ // `finally` so a pass that throws still closes its bracket — a listener that opened
654
+ // a scope on begin would otherwise attribute the rest of the frame to the pass that
655
+ // died.
656
+ if (announce_passes) {
657
+ this.onPassEnd.send2(node, this);
658
+ }
659
+ }
660
+
661
+
662
+ const resource_entries = this.__resource_registry;
663
+ const resource_entry_count = resource_entries.length;
664
+
665
+ for (let entry_index = 0; entry_index < resource_entry_count; entry_index++) {
666
+ const entry = resource_entries[entry_index];
667
+
668
+ if (entry.last === node && entry.isTransient()) {
669
+ // this was the last user of the resource and the resource is transient (no external usage)
670
+ entry.destroy(context.resource_manager);
671
+ }
672
+
673
+ }
674
+
675
+ }
676
+
677
+ this.onExecuted.send2(context, this);
678
+ }
679
+
680
+ /**
681
+ * Should only call after {@link compile}
682
+ *
683
+ * One entry in `resources` per resource **node**, that is per version, keyed by node ID. A pass
684
+ * records the node it read or wrote and not the underlying resource, so this is the id space
685
+ * `passes[].reads` and `passes[].writes` point into; a registry-keyed export would leave those
686
+ * references dangling on every version past the first.
687
+ *
688
+ * Can be visualized with this tool: https://skaarj1989.github.io/FrameGraph/
689
+ *
690
+ * @see https://github.com/skaarj1989/FrameGraph/blob/viewer/JsonWriter.hpp
691
+ * @return {{passes: [], resources: []}}
692
+ */
693
+ exportToJson() {
694
+
695
+ const resources = this.__resource_nodes.map(node => {
696
+ const entry = this.__resource_registry[node.resource_id];
697
+
698
+ const j = {
699
+ id: node.id,
700
+ name: node.name,
701
+ transient: entry.isTransient(),
702
+ };
703
+
704
+ if (node.version > 0) {
705
+ j.version = node.version;
706
+ }
707
+
708
+ const description = entry.resource_descriptor?.toString();
709
+
710
+ if (description) {
711
+ j.description = description;
712
+ }
713
+
714
+ if (node.producer !== null) {
715
+ j.createdBy = node.producer.id;
716
+ }
717
+
718
+ const readers = [];
719
+ const writers = [];
720
+
721
+ for (const pass of this.__pass_nodes) {
722
+ if (pass.reads(node.id)) {
723
+ readers.push(pass.id);
724
+ }
725
+ if (pass.writes(node.id)) {
726
+ writers.push(pass.id);
727
+ }
728
+ }
729
+
730
+ if (readers.length > 0) {
731
+ j.readers = readers;
732
+ }
733
+ if (writers.length > 0) {
734
+ j.writers = writers;
735
+ }
736
+
737
+ return j;
738
+ });
739
+
740
+ return {
741
+ passes: this.__pass_nodes.map(node => {
742
+ return {
743
+ id: node.id,
744
+ name: node.name,
745
+ culled: !node.can_execute(),
746
+ reads: node.resource_reads,
747
+ writes: node.resource_writes,
748
+ }
749
+ }),
750
+ resources
751
+ }
752
+ }
753
+
754
+ /**
755
+ * Export the graph diagram in GraphViz DOT format.
756
+ * Useful for debugging.
757
+ * @see https://en.wikipedia.org/wiki/DOT_(graph_description_language)
758
+ * @return {string}
759
+ */
760
+ exportToDot() {
761
+ const out = new LineBuilder();
762
+
763
+ out.add("digraph FrameGraph {");
764
+ out.indent();
765
+ out.add("graph [style=invis, rankdir=\"TB\" ordering=out, splines=spline]");
766
+ out.add("node [shape=record, fontname=\"helvetica\", fontsize=10, margin=\"0.2,0.03\"]");
767
+
768
+
769
+ // -- Define pass nodes
770
+
771
+ /**
772
+ * @param {FramePassNode} node
773
+ * @returns {string}
774
+ */
775
+ const pass_declaration = (node) => `P${node.id} [label=<{ {<B>${node.name}</B>} | {${(node.has_side_effects ? "&#x2605; " : "")} Refs: ${node.ref_count}<BR/> Index: ${node.id}} }> style=\"rounded,filled\", fillcolor=${(node.ref_count > 0 || node.has_side_effects) ? "orange" : "lightgray"}]`;
776
+
777
+ out.add("");
778
+ out.add("# Pass Nodes");
779
+ for (const node of this.__pass_nodes) {
780
+ if (node.scope === null) {
781
+ out.add(pass_declaration(node));
782
+ }
783
+ }
784
+
785
+ // -- Define resource nodes
786
+
787
+ /**
788
+ *
789
+ * @param {string} type
790
+ * @returns {number}
791
+ */
792
+ function type_to_color(type) {
793
+
794
+ const hash_32 = computeStringHash(type);
795
+
796
+ const hash_16 = (hash_32 & 0xffff) ^ (hash_32 >>> 16);
797
+
798
+ return hash_16 / 0xffff;
799
+ }
800
+
801
+ /**
802
+ *
803
+ * @param {ResourceEntry} entry
804
+ * @return {string}
805
+ */
806
+ function entry_to_background_color(entry) {
807
+
808
+ let color_hue = type_to_color(entry.resource_descriptor?.type ?? "");
809
+
810
+ // remap hue away from red
811
+ color_hue = lerp(0.10, 0.90, color_hue);
812
+
813
+ const saturation = entry.isImported() ? 0.2 : 0.07;
814
+
815
+ const color_rgb = [];
816
+ okhsv_to_linear_srgb(color_rgb, color_hue, saturation, 1)
817
+
818
+ const hex = rgb_to_hex(...color_rgb.map(float_to_uint8));
819
+
820
+ return `#${hex}`;
821
+ }
822
+
823
+ /**
824
+ * @param {ResourceNode} node
825
+ * @returns {string}
826
+ */
827
+ const resource_declaration = (node) => {
828
+ const entry = this.__resource_registry[node.resource_id];
829
+
830
+ const type = graphviz_escape_string(entry.resource_descriptor?.type ?? "");
831
+
832
+ const block_title = `${entry.isImported() ? "↪ " : ""}${node.name}`;
833
+ const block_tooltip = graphviz_escape_string(entry.toString());
834
+
835
+ return `R${entry.resource_id}_${node.version} [label=<{ {<B>${block_title}</B>${node.version > 0 ? ` <FONT>v${(node.version + 1)}</FONT>` : ""}<BR/>${type}} | {Index: ${entry.resource_id}<BR/> Refs : ${node.ref_count} } }> style=filled, fillcolor="${entry_to_background_color(entry)}" tooltip="${block_tooltip}"]`;
836
+ };
837
+
838
+ out.add("");
839
+ out.add("# Resource Nodes");
840
+ for (const node of this.__resource_nodes) {
841
+ if (node.scope === null) {
842
+ out.add(resource_declaration(node));
843
+ }
844
+ }
845
+
846
+ // -- Define scoped nodes, inside the cluster of the scope that recorded them
847
+
848
+ /**
849
+ * Scopes that stamped at least one node, and the scopes enclosing those.
850
+ * @type {Set<FrameGraphScope>}
851
+ */
852
+ const scopes = new Set();
853
+
854
+ for (const node of [...this.__pass_nodes, ...this.__resource_nodes]) {
855
+ for (let scope = node.scope; scope !== null && !scopes.has(scope); scope = scope.parent) {
856
+ scopes.add(scope);
857
+ }
858
+ }
859
+
860
+ /**
861
+ * @param {FrameGraphScope} scope
862
+ */
863
+ const emit_scope = (scope) => {
864
+ out.add(`subgraph cluster_${scope.id} {`);
865
+ out.indent();
866
+
867
+ // the root graph is drawn invisible, so a cluster has to ask for its own frame
868
+ out.add(`label="${graphviz_escape_string(scope.name)}"`);
869
+ out.add("style=\"rounded,dashed\", color=\"gray55\", fontname=\"helvetica\", fontsize=10, labeljust=\"l\"");
870
+
871
+ for (const node of this.__pass_nodes) {
872
+ if (node.scope === scope) {
873
+ out.add(pass_declaration(node));
874
+ }
875
+ }
876
+
877
+ for (const node of this.__resource_nodes) {
878
+ if (node.scope === scope) {
879
+ out.add(resource_declaration(node));
880
+ }
881
+ }
882
+
883
+ for (const child of scopes) {
884
+ if (child.parent === scope) {
885
+ emit_scope(child);
886
+ }
887
+ }
888
+
889
+ out.dedent();
890
+ out.add("}");
891
+ };
892
+
893
+ if (scopes.size > 0) {
894
+ out.add("");
895
+ out.add("# Scopes");
896
+
897
+ for (const scope of scopes) {
898
+ if (scope.parent === null) {
899
+ emit_scope(scope);
900
+ }
901
+ }
902
+ }
903
+
904
+ // -- Each pass node points to the resource that it writes
905
+
906
+ out.add("");
907
+ out.add("# Resource Writes");
908
+ for (const node of this.__pass_nodes) {
909
+ out.add(`P${node.id} -> {`);
910
+ out.indent();
911
+ for (const id of node.resource_writes) {
912
+ const written = this.__resource_nodes[id];
913
+ out.add(`R${written.resource_id}_${written.version} `);
914
+ }
915
+ out.dedent();
916
+ out.add("} [color=orangered]");
917
+ }
918
+
919
+ // -- Each resource node points to the pass where it's consumed
920
+
921
+ out.add("");
922
+ out.add("# Resource Reads");
923
+
924
+ for (const node of this.__resource_nodes) {
925
+ out.add(`R${node.resource_id}_${node.version} -> {`);
926
+ out.indent();
927
+ // find all readers of this resource node
928
+ for (const pass of this.__pass_nodes) {
929
+ for (const id of pass.resource_reads)
930
+ if (id === node.id) {
931
+ out.add(`P${pass.id} `);
932
+ }
933
+ }
934
+ out.dedent();
935
+ out.add("} [color=olivedrab3]");
936
+ }
937
+ out.dedent();
938
+ out.add("}");
939
+ return out.build();
940
+ }
941
+ }
942
+
943
+ /**
944
+ * Type check shortcut.
945
+ * @readonly
946
+ * @type {boolean}
947
+ */
901
948
  FrameGraph.prototype.isFrameGraph = true;