@onerjs/core 9.0.0 → 9.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. package/Animations/animationGroup.pure.js.map +1 -1
  2. package/Cameras/targetCamera.pure.js +6 -1
  3. package/Cameras/targetCamera.pure.js.map +1 -1
  4. package/Engines/Native/nativeHardwareTexture.d.ts +8 -1
  5. package/Engines/Native/nativeHardwareTexture.js +7 -0
  6. package/Engines/Native/nativeHardwareTexture.js.map +1 -1
  7. package/Engines/Native/nativeHelpers.js +56 -1
  8. package/Engines/Native/nativeHelpers.js.map +1 -1
  9. package/Engines/Native/nativeInterfaces.d.ts +37 -8
  10. package/Engines/Native/nativeInterfaces.js.map +1 -1
  11. package/Engines/Native/nativeRenderTargetWrapper.d.ts +40 -0
  12. package/Engines/Native/nativeRenderTargetWrapper.js +265 -5
  13. package/Engines/Native/nativeRenderTargetWrapper.js.map +1 -1
  14. package/Engines/Processors/shaderCodeCursor.d.ts +1 -0
  15. package/Engines/Processors/shaderCodeCursor.js +40 -3
  16. package/Engines/Processors/shaderCodeCursor.js.map +1 -1
  17. package/Engines/WebGPU/Extensions/engine.renderTarget.pure.js +1 -0
  18. package/Engines/WebGPU/Extensions/engine.renderTarget.pure.js.map +1 -1
  19. package/Engines/abstractEngine.pure.d.ts +15 -1
  20. package/Engines/abstractEngine.pure.js +12 -4
  21. package/Engines/abstractEngine.pure.js.map +1 -1
  22. package/Engines/engine.pure.d.ts +0 -1
  23. package/Engines/engine.pure.js +0 -7
  24. package/Engines/engine.pure.js.map +1 -1
  25. package/Engines/nativeEngine.pure.js +1 -1
  26. package/Engines/nativeEngine.pure.js.map +1 -1
  27. package/Engines/thinEngine.pure.d.ts +1 -0
  28. package/Engines/thinEngine.pure.js +13 -2
  29. package/Engines/thinEngine.pure.js.map +1 -1
  30. package/Engines/thinNativeEngine.pure.d.ts +217 -17
  31. package/Engines/thinNativeEngine.pure.js +1431 -164
  32. package/Engines/thinNativeEngine.pure.js.map +1 -1
  33. package/Engines/thinWebGPUEngine.js +6 -1
  34. package/Engines/thinWebGPUEngine.js.map +1 -1
  35. package/Engines/webgpuEngine.pure.d.ts +12 -1
  36. package/Engines/webgpuEngine.pure.js +8 -0
  37. package/Engines/webgpuEngine.pure.js.map +1 -1
  38. package/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.pure.d.ts +10 -0
  39. package/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.pure.js +24 -12
  40. package/FlowGraph/Blocks/Data/Transformers/flowGraphJsonPointerParserBlock.pure.js.map +1 -1
  41. package/FlowGraph/Blocks/Data/flowGraphGetPropertyBlock.pure.d.ts +7 -0
  42. package/FlowGraph/Blocks/Data/flowGraphGetPropertyBlock.pure.js +43 -0
  43. package/FlowGraph/Blocks/Data/flowGraphGetPropertyBlock.pure.js.map +1 -1
  44. package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.d.ts +16 -4
  45. package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.js +20 -6
  46. package/FlowGraph/Blocks/Event/flowGraphSceneTickEventBlock.pure.js.map +1 -1
  47. package/FlowGraph/Blocks/Execution/Animation/flowGraphInterpolationBlock.pure.js +3 -3
  48. package/FlowGraph/Blocks/Execution/Animation/flowGraphInterpolationBlock.pure.js.map +1 -1
  49. package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.d.ts +7 -7
  50. package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.js +52 -35
  51. package/FlowGraph/Blocks/Execution/Animation/flowGraphPlayAnimationBlock.pure.js.map +1 -1
  52. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphCancelDelayBlock.pure.d.ts +12 -1
  53. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphCancelDelayBlock.pure.js +8 -0
  54. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphCancelDelayBlock.pure.js.map +1 -1
  55. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphForLoopBlock.pure.js +4 -1
  56. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphForLoopBlock.pure.js.map +1 -1
  57. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphThrottleBlock.pure.d.ts +12 -1
  58. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphThrottleBlock.pure.js +5 -1
  59. package/FlowGraph/Blocks/Execution/ControlFlow/flowGraphThrottleBlock.pure.js.map +1 -1
  60. package/FlowGraph/Blocks/Execution/flowGraphSetPropertyBlock.pure.d.ts +2 -0
  61. package/FlowGraph/Blocks/Execution/flowGraphSetPropertyBlock.pure.js +14 -2
  62. package/FlowGraph/Blocks/Execution/flowGraphSetPropertyBlock.pure.js.map +1 -1
  63. package/FlowGraph/Blocks/Execution/flowGraphSetVariableBlock.pure.d.ts +5 -1
  64. package/FlowGraph/Blocks/Execution/flowGraphSetVariableBlock.pure.js +23 -8
  65. package/FlowGraph/Blocks/Execution/flowGraphSetVariableBlock.pure.js.map +1 -1
  66. package/FlowGraph/CustomTypes/flowGraphInteger.pure.d.ts +5 -0
  67. package/FlowGraph/CustomTypes/flowGraphInteger.pure.js +7 -0
  68. package/FlowGraph/CustomTypes/flowGraphInteger.pure.js.map +1 -1
  69. package/FlowGraph/CustomTypes/flowGraphMatrix.d.ts +10 -0
  70. package/FlowGraph/CustomTypes/flowGraphMatrix.js +14 -0
  71. package/FlowGraph/CustomTypes/flowGraphMatrix.js.map +1 -1
  72. package/FlowGraph/flowGraph.d.ts +2 -1
  73. package/FlowGraph/flowGraph.js +3 -2
  74. package/FlowGraph/flowGraph.js.map +1 -1
  75. package/FlowGraph/flowGraphAnimationHelpers.d.ts +24 -0
  76. package/FlowGraph/flowGraphAnimationHelpers.js +21 -0
  77. package/FlowGraph/flowGraphAnimationHelpers.js.map +1 -0
  78. package/FlowGraph/flowGraphContext.js +1 -0
  79. package/FlowGraph/flowGraphContext.js.map +1 -1
  80. package/FlowGraph/flowGraphValidator.js +0 -4
  81. package/FlowGraph/flowGraphValidator.js.map +1 -1
  82. package/FlowGraph/index.d.ts +2 -0
  83. package/FlowGraph/index.js +2 -0
  84. package/FlowGraph/index.js.map +1 -1
  85. package/FlowGraph/pure.d.ts +2 -0
  86. package/FlowGraph/pure.js +2 -0
  87. package/FlowGraph/pure.js.map +1 -1
  88. package/FlowGraph/serialization.d.ts +2 -2
  89. package/FlowGraph/serialization.js +2 -2
  90. package/FlowGraph/serialization.js.map +1 -1
  91. package/FlowGraph/typeDefinitions.d.ts +1 -1
  92. package/FlowGraph/typeDefinitions.js.map +1 -1
  93. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.d.ts +1 -0
  94. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.js +4 -0
  95. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.js.map +1 -0
  96. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.pure.d.ts +54 -0
  97. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.pure.js +155 -0
  98. package/FrameGraph/Node/Blocks/Textures/minMaxReducerBlock.pure.js.map +1 -0
  99. package/FrameGraph/Node/Blocks/Textures/pure.d.ts +1 -0
  100. package/FrameGraph/Node/Blocks/Textures/pure.js +1 -0
  101. package/FrameGraph/Node/Blocks/Textures/pure.js.map +1 -1
  102. package/FrameGraph/Node/Blocks/allBlocks.pure.js +2 -0
  103. package/FrameGraph/Node/Blocks/allBlocks.pure.js.map +1 -1
  104. package/FrameGraph/Node/Blocks/index.d.ts +1 -0
  105. package/FrameGraph/Node/Blocks/index.js +1 -0
  106. package/FrameGraph/Node/Blocks/index.js.map +1 -1
  107. package/FrameGraph/Node/Blocks/pure.d.ts +1 -0
  108. package/FrameGraph/Node/Blocks/pure.js +1 -0
  109. package/FrameGraph/Node/Blocks/pure.js.map +1 -1
  110. package/FrameGraph/Tasks/Misc/minMaxReducerTask.d.ts +62 -0
  111. package/FrameGraph/Tasks/Misc/minMaxReducerTask.js +159 -0
  112. package/FrameGraph/Tasks/Misc/minMaxReducerTask.js.map +1 -0
  113. package/FrameGraph/Tasks/Rendering/geometryRendererTask.js +3 -1
  114. package/FrameGraph/Tasks/Rendering/geometryRendererTask.js.map +1 -1
  115. package/FrameGraph/frameGraph.d.ts +4 -0
  116. package/FrameGraph/frameGraph.js +14 -0
  117. package/FrameGraph/frameGraph.js.map +1 -1
  118. package/FrameGraph/index.d.ts +1 -0
  119. package/FrameGraph/index.js +1 -0
  120. package/FrameGraph/index.js.map +1 -1
  121. package/FrameGraph/pure.d.ts +1 -0
  122. package/FrameGraph/pure.js +1 -0
  123. package/FrameGraph/pure.js.map +1 -1
  124. package/Inputs/scene.inputManager.js +1 -1
  125. package/Inputs/scene.inputManager.js.map +1 -1
  126. package/Lights/Clustered/clusteredLightingSceneComponent.pure.d.ts +5 -0
  127. package/Lights/Clustered/clusteredLightingSceneComponent.pure.js +22 -1
  128. package/Lights/Clustered/clusteredLightingSceneComponent.pure.js.map +1 -1
  129. package/Lights/Shadows/cascadedShadowGenerator.pure.js +7 -3
  130. package/Lights/Shadows/cascadedShadowGenerator.pure.js.map +1 -1
  131. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.d.ts +7 -0
  132. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.js +8 -0
  133. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.js.map +1 -0
  134. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.pure.d.ts +54 -0
  135. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.pure.js +81 -0
  136. package/Materials/GaussianSplatting/gaussianPointSplattingBlitMaterial.pure.js.map +1 -0
  137. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.d.ts +7 -0
  138. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.js +8 -0
  139. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.js.map +1 -0
  140. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.pure.d.ts +60 -0
  141. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.pure.js +90 -0
  142. package/Materials/GaussianSplatting/gaussianPointSplattingDepthBlitMaterial.pure.js.map +1 -0
  143. package/Materials/GaussianSplatting/gaussianSplattingDebugMaterialPlugin.pure.d.ts +31 -0
  144. package/Materials/GaussianSplatting/gaussianSplattingDebugMaterialPlugin.pure.js +98 -0
  145. package/Materials/GaussianSplatting/gaussianSplattingDebugMaterialPlugin.pure.js.map +1 -1
  146. package/Materials/GaussianSplatting/gaussianSplattingGpuPickingMaterialPlugin.pure.js +1 -1
  147. package/Materials/GaussianSplatting/gaussianSplattingGpuPickingMaterialPlugin.pure.js.map +1 -1
  148. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js +2 -2
  149. package/Materials/GaussianSplatting/gaussianSplattingMaterial.pure.js.map +1 -1
  150. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.d.ts +1 -0
  151. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.js +4 -0
  152. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.js.map +1 -0
  153. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.pure.d.ts +83 -0
  154. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.pure.js +191 -0
  155. package/Materials/GaussianSplatting/gaussianSplattingOverdrawMaterialPlugin.pure.js.map +1 -0
  156. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.d.ts +1 -0
  157. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.js +4 -0
  158. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.js.map +1 -0
  159. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.pure.d.ts +62 -0
  160. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.pure.js +125 -0
  161. package/Materials/GaussianSplatting/gaussianSplattingSizeMaterialPlugin.pure.js.map +1 -0
  162. package/Materials/GaussianSplatting/pure.d.ts +4 -0
  163. package/Materials/GaussianSplatting/pure.js +4 -0
  164. package/Materials/GaussianSplatting/pure.js.map +1 -1
  165. package/Materials/Node/Blocks/Dual/imageSourceBlock.pure.d.ts +1 -0
  166. package/Materials/Node/Blocks/Dual/imageSourceBlock.pure.js +12 -0
  167. package/Materials/Node/Blocks/Dual/imageSourceBlock.pure.js.map +1 -1
  168. package/Materials/Textures/Procedurals/proceduralTexture.pure.d.ts +1 -0
  169. package/Materials/Textures/Procedurals/proceduralTexture.pure.js +31 -7
  170. package/Materials/Textures/Procedurals/proceduralTexture.pure.js.map +1 -1
  171. package/Materials/Textures/mirrorTexture.pure.js +3 -1
  172. package/Materials/Textures/mirrorTexture.pure.js.map +1 -1
  173. package/Materials/Textures/texture.pure.js +8 -2
  174. package/Materials/Textures/texture.pure.js.map +1 -1
  175. package/Materials/effectRenderer.pure.d.ts +4 -0
  176. package/Materials/effectRenderer.pure.js +3 -2
  177. package/Materials/effectRenderer.pure.js.map +1 -1
  178. package/Materials/index.d.ts +4 -0
  179. package/Materials/index.js +4 -0
  180. package/Materials/index.js.map +1 -1
  181. package/Materials/material.pure.js +25 -12
  182. package/Materials/material.pure.js.map +1 -1
  183. package/Materials/pure.d.ts +4 -0
  184. package/Materials/pure.js +4 -0
  185. package/Materials/pure.js.map +1 -1
  186. package/Materials/standardMaterial.pure.d.ts +8 -0
  187. package/Materials/standardMaterial.pure.js +24 -2
  188. package/Materials/standardMaterial.pure.js.map +1 -1
  189. package/Meshes/Builders/polygonBuilder.pure.js +1 -1
  190. package/Meshes/Builders/polygonBuilder.pure.js.map +1 -1
  191. package/Meshes/GaussianSplatting/gaussianPointSplattingController.d.ts +8 -0
  192. package/Meshes/GaussianSplatting/gaussianPointSplattingController.js +11 -0
  193. package/Meshes/GaussianSplatting/gaussianPointSplattingController.js.map +1 -0
  194. package/Meshes/GaussianSplatting/gaussianPointSplattingController.pure.d.ts +231 -0
  195. package/Meshes/GaussianSplatting/gaussianPointSplattingController.pure.js +1212 -0
  196. package/Meshes/GaussianSplatting/gaussianPointSplattingController.pure.js.map +1 -0
  197. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.d.ts +14 -0
  198. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.js +15 -0
  199. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.js.map +1 -0
  200. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.pure.d.ts +285 -0
  201. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.pure.js +812 -0
  202. package/Meshes/GaussianSplatting/gaussianPointSplattingRenderer.pure.js.map +1 -0
  203. package/Meshes/GaussianSplatting/gaussianSplattingMesh.d.ts +1 -0
  204. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js +2 -0
  205. package/Meshes/GaussianSplatting/gaussianSplattingMesh.js.map +1 -1
  206. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.d.ts +160 -0
  207. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js +219 -0
  208. package/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.js.map +1 -1
  209. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.d.ts +32 -4
  210. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js +114 -21
  211. package/Meshes/GaussianSplatting/gaussianSplattingMeshBase.pure.js.map +1 -1
  212. package/Meshes/GaussianSplatting/pure.d.ts +2 -0
  213. package/Meshes/GaussianSplatting/pure.js +2 -0
  214. package/Meshes/GaussianSplatting/pure.js.map +1 -1
  215. package/Meshes/Node/Blocks/booleanGeometryBlock.pure.js +25 -11
  216. package/Meshes/Node/Blocks/booleanGeometryBlock.pure.js.map +1 -1
  217. package/Meshes/abstractMesh.pure.d.ts +12 -0
  218. package/Meshes/abstractMesh.pure.js +32 -0
  219. package/Meshes/abstractMesh.pure.js.map +1 -1
  220. package/Meshes/index.d.ts +1 -0
  221. package/Meshes/index.js +1 -0
  222. package/Meshes/index.js.map +1 -1
  223. package/Meshes/pure.d.ts +1 -0
  224. package/Meshes/pure.js +1 -0
  225. package/Meshes/pure.js.map +1 -1
  226. package/Meshes/subMesh.pure.d.ts +11 -2
  227. package/Meshes/subMesh.pure.js +20 -5
  228. package/Meshes/subMesh.pure.js.map +1 -1
  229. package/Meshes/thinInstanceMesh.pure.js +1 -0
  230. package/Meshes/thinInstanceMesh.pure.js.map +1 -1
  231. package/Misc/andOrNotEvaluator.js +4 -0
  232. package/Misc/andOrNotEvaluator.js.map +1 -1
  233. package/Misc/minMaxReducer.pure.d.ts +6 -0
  234. package/Misc/minMaxReducer.pure.js +10 -0
  235. package/Misc/minMaxReducer.pure.js.map +1 -1
  236. package/Misc/thinMinMaxReducer.d.ts +19 -2
  237. package/Misc/thinMinMaxReducer.js +74 -21
  238. package/Misc/thinMinMaxReducer.js.map +1 -1
  239. package/Misc/tools.pure.js +1 -1
  240. package/Misc/tools.pure.js.map +1 -1
  241. package/Particles/Node/Blocks/Update/updateNoiseBlock.pure.js +19 -7
  242. package/Particles/Node/Blocks/Update/updateNoiseBlock.pure.js.map +1 -1
  243. package/Particles/Node/Blocks/particleSourceTextureBlock.pure.js +3 -2
  244. package/Particles/Node/Blocks/particleSourceTextureBlock.pure.js.map +1 -1
  245. package/Particles/gpuParticleSystem.pure.d.ts +2 -0
  246. package/Particles/gpuParticleSystem.pure.js +12 -4
  247. package/Particles/gpuParticleSystem.pure.js.map +1 -1
  248. package/Particles/particle.js +3 -2
  249. package/Particles/particle.js.map +1 -1
  250. package/Particles/solidParticleSystem.js +7 -1
  251. package/Particles/solidParticleSystem.js.map +1 -1
  252. package/Particles/thinParticleSystem.function.d.ts +6 -0
  253. package/Particles/thinParticleSystem.function.js +144 -88
  254. package/Particles/thinParticleSystem.function.js.map +1 -1
  255. package/Particles/thinParticleSystem.pure.d.ts +6 -0
  256. package/Particles/thinParticleSystem.pure.js +70 -9
  257. package/Particles/thinParticleSystem.pure.js.map +1 -1
  258. package/Physics/v2/ragdoll.d.ts +2 -4
  259. package/Physics/v2/ragdoll.js +11 -4
  260. package/Physics/v2/ragdoll.js.map +1 -1
  261. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.d.ts +12 -0
  262. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.js +43 -18
  263. package/Rendering/IBLShadows/iblShadowsVoxelRenderer.pure.js.map +1 -1
  264. package/Rendering/depthRenderer.pure.d.ts +9 -0
  265. package/Rendering/depthRenderer.pure.js +12 -0
  266. package/Rendering/depthRenderer.pure.js.map +1 -1
  267. package/Rendering/fluidRenderer/fluidRenderingObject.d.ts +4 -0
  268. package/Rendering/fluidRenderer/fluidRenderingObject.js +6 -0
  269. package/Rendering/fluidRenderer/fluidRenderingObject.js.map +1 -1
  270. package/Rendering/fluidRenderer/fluidRenderingObjectParticleSystem.d.ts +4 -0
  271. package/Rendering/fluidRenderer/fluidRenderingObjectParticleSystem.js +8 -0
  272. package/Rendering/fluidRenderer/fluidRenderingObjectParticleSystem.js.map +1 -1
  273. package/Rendering/fluidRenderer/fluidRenderingTargetRenderer.js +3 -0
  274. package/Rendering/fluidRenderer/fluidRenderingTargetRenderer.js.map +1 -1
  275. package/Rendering/geometryBufferRenderer.pure.js +4 -1
  276. package/Rendering/geometryBufferRenderer.pure.js.map +1 -1
  277. package/Rendering/objectRenderer.js.map +1 -1
  278. package/Rendering/prePassRenderer.pure.js +3 -3
  279. package/Rendering/prePassRenderer.pure.js.map +1 -1
  280. package/Shaders/ShadersInclude/gaussianSplatting.js +3 -1
  281. package/Shaders/ShadersInclude/gaussianSplatting.js.map +1 -1
  282. package/Shaders/ShadersInclude/lightsFragmentFunctions.js +27 -3
  283. package/Shaders/ShadersInclude/lightsFragmentFunctions.js.map +1 -1
  284. package/Shaders/ShadersInclude/oitDeclaration.js +1 -1
  285. package/Shaders/ShadersInclude/oitDeclaration.js.map +1 -1
  286. package/Shaders/minmaxRedux.fragment.js +6 -1
  287. package/Shaders/minmaxRedux.fragment.js.map +1 -1
  288. package/ShadersWGSL/ShadersInclude/gaussianPointSplatting.d.ts +5 -0
  289. package/ShadersWGSL/ShadersInclude/gaussianPointSplatting.js +65 -0
  290. package/ShadersWGSL/ShadersInclude/gaussianPointSplatting.js.map +1 -0
  291. package/ShadersWGSL/ShadersInclude/gaussianPointSplattingScan.d.ts +5 -0
  292. package/ShadersWGSL/ShadersInclude/gaussianPointSplattingScan.js +17 -0
  293. package/ShadersWGSL/ShadersInclude/gaussianPointSplattingScan.js.map +1 -0
  294. package/ShadersWGSL/ShadersInclude/gaussianSplatting.d.ts +1 -0
  295. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js +6 -78
  296. package/ShadersWGSL/ShadersInclude/gaussianSplatting.js.map +1 -1
  297. package/ShadersWGSL/ShadersInclude/gaussianSplattingFragmentDeclaration.js +2 -2
  298. package/ShadersWGSL/ShadersInclude/gaussianSplattingFragmentDeclaration.js.map +1 -1
  299. package/ShadersWGSL/ShadersInclude/gaussianSplattingShared.d.ts +5 -0
  300. package/ShadersWGSL/ShadersInclude/gaussianSplattingShared.js +79 -0
  301. package/ShadersWGSL/ShadersInclude/gaussianSplattingShared.js.map +1 -0
  302. package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js +27 -3
  303. package/ShadersWGSL/ShadersInclude/lightsFragmentFunctions.js.map +1 -1
  304. package/ShadersWGSL/gaussianPointSplattingBlit.fragment.d.ts +5 -0
  305. package/ShadersWGSL/gaussianPointSplattingBlit.fragment.js +22 -0
  306. package/ShadersWGSL/gaussianPointSplattingBlit.fragment.js.map +1 -0
  307. package/ShadersWGSL/gaussianPointSplattingBlit.vertex.d.ts +5 -0
  308. package/ShadersWGSL/gaussianPointSplattingBlit.vertex.js +19 -0
  309. package/ShadersWGSL/gaussianPointSplattingBlit.vertex.js.map +1 -0
  310. package/ShadersWGSL/gaussianPointSplattingDepthBlit.fragment.d.ts +5 -0
  311. package/ShadersWGSL/gaussianPointSplattingDepthBlit.fragment.js +21 -0
  312. package/ShadersWGSL/gaussianPointSplattingDepthBlit.fragment.js.map +1 -0
  313. package/ShadersWGSL/gaussianSplatting.vertex.js +2 -1
  314. package/ShadersWGSL/gaussianSplatting.vertex.js.map +1 -1
  315. package/ShadersWGSL/gaussianSplattingDepth.vertex.js +2 -1
  316. package/ShadersWGSL/gaussianSplattingDepth.vertex.js.map +1 -1
  317. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js +2 -1
  318. package/ShadersWGSL/gaussianSplattingVoxel.vertex.js.map +1 -1
  319. package/ShadersWGSL/gpsHiZBuild.compute.d.ts +5 -0
  320. package/ShadersWGSL/gpsHiZBuild.compute.js +16 -0
  321. package/ShadersWGSL/gpsHiZBuild.compute.js.map +1 -0
  322. package/ShadersWGSL/gpsPartition.compute.d.ts +5 -0
  323. package/ShadersWGSL/gpsPartition.compute.js +27 -0
  324. package/ShadersWGSL/gpsPartition.compute.js.map +1 -0
  325. package/ShadersWGSL/gpsPreprocess.compute.d.ts +5 -0
  326. package/ShadersWGSL/gpsPreprocess.compute.js +86 -0
  327. package/ShadersWGSL/gpsPreprocess.compute.js.map +1 -0
  328. package/ShadersWGSL/gpsResolve.compute.d.ts +5 -0
  329. package/ShadersWGSL/gpsResolve.compute.js +41 -0
  330. package/ShadersWGSL/gpsResolve.compute.js.map +1 -0
  331. package/ShadersWGSL/gpsScanAdd.compute.d.ts +5 -0
  332. package/ShadersWGSL/gpsScanAdd.compute.js +22 -0
  333. package/ShadersWGSL/gpsScanAdd.compute.js.map +1 -0
  334. package/ShadersWGSL/gpsScanBlocks.compute.d.ts +5 -0
  335. package/ShadersWGSL/gpsScanBlocks.compute.js +23 -0
  336. package/ShadersWGSL/gpsScanBlocks.compute.js.map +1 -0
  337. package/ShadersWGSL/gpsScanSums.compute.d.ts +5 -0
  338. package/ShadersWGSL/gpsScanSums.compute.js +30 -0
  339. package/ShadersWGSL/gpsScanSums.compute.js.map +1 -0
  340. package/ShadersWGSL/gpsSplat.compute.d.ts +5 -0
  341. package/ShadersWGSL/gpsSplat.compute.js +37 -0
  342. package/ShadersWGSL/gpsSplat.compute.js.map +1 -0
  343. package/ShadersWGSL/minmaxRedux.fragment.js +20 -3
  344. package/ShadersWGSL/minmaxRedux.fragment.js.map +1 -1
  345. package/XR/features/WebXRSpaceWarp.pure.js +2 -1
  346. package/XR/features/WebXRSpaceWarp.pure.js.map +1 -1
  347. package/assetContainer.d.ts +1 -0
  348. package/assetContainer.js +5 -1
  349. package/assetContainer.js.map +1 -1
  350. package/package.json +1 -1
  351. package/scene.pure.d.ts +13 -2
  352. package/scene.pure.js +28 -4
  353. package/scene.pure.js.map +1 -1
@@ -1,6 +1,8 @@
1
1
  /** This file must only contain pure code and pure imports */
2
2
  import { RegisterBufferAlign } from "../Buffers/buffer.align.pure.js";
3
3
  import { InternalTexture } from "../Materials/Textures/internalTexture.js";
4
+ import { HasStencilAspect } from "../Materials/Textures/textureHelper.functions.js";
5
+ import { _IESTextureLoader } from "../Materials/Textures/Loaders/iesTextureLoader.js";
4
6
  import { DataBuffer } from "../Buffers/dataBuffer.js";
5
7
  import { Observable } from "../Misc/observable.pure.js";
6
8
  import { Logger } from "../Misc/logger.js";
@@ -117,6 +119,16 @@ class CommandBufferEncoder {
117
119
  }
118
120
  }
119
121
  const remappedAttributesNames = [];
122
+ // Reused by bindFramebuffer(forceFullscreenViewport) to avoid allocating a viewport per bind.
123
+ const _FullscreenViewport = { x: 0, y: 0, width: 1, height: 1 };
124
+ // Render-target textures created with mip storage (createMipMaps or generateMipMaps). Tracked separately from
125
+ // generateMipMaps so sample-count changes and context-loss rebuilds keep an explicitly authored mip chain.
126
+ const _TexturesWithMipStorage = /*#__PURE__*/ new WeakSet();
127
+ /**
128
+ * Sentinel for "no attachment masking": every color attachment of the bound framebuffer takes part in
129
+ * the clear. Native treats this value specially and skips the bgfx color-palette clear path.
130
+ */
131
+ const _AllAttachmentsMask = 0xff;
120
132
  /** @internal */
121
133
  export class ThinNativeEngine extends ThinEngine {
122
134
  /** @internal */
@@ -124,7 +136,9 @@ export class ThinNativeEngine extends ThinEngine {
124
136
  return new NativeDataStream();
125
137
  }
126
138
  constructor(options = {}) {
127
- super(null, false, undefined, options.adaptToDeviceRatio);
139
+ super(null, false, options, options.adaptToDeviceRatio);
140
+ this._transformFeedbackBuffer = null;
141
+ this._transformFeedbackActive = false;
128
142
  this._initializeNativeEngine(options.adaptToDeviceRatio ?? false);
129
143
  }
130
144
  //////////////////////////////////////////////////////////////////////
@@ -145,8 +159,24 @@ export class ThinNativeEngine extends ThinEngine {
145
159
  });
146
160
  this._camera = _native.Camera ? new _native.Camera() : null;
147
161
  this._commandBufferEncoder = new CommandBufferEncoder(this._engine);
162
+ this._frameGraphFramebufferRefCount = new Map();
148
163
  this._frameStats = { gpuTimeNs: Number.NaN };
164
+ // There is no DOM canvas to draw the default loading screen into, so install a no-op one; this keeps
165
+ // the loading-UI accessors (loadingUIText, loadingUIBackgroundColor, display/hide) usable on native.
166
+ let loadingScreenWarningDisplayed = false;
167
+ this._loadingScreen = {
168
+ displayLoadingUI: () => {
169
+ if (!loadingScreenWarningDisplayed) {
170
+ Logger.Warn("NativeEngine is unable to display the loading screen UI.");
171
+ loadingScreenWarningDisplayed = true;
172
+ }
173
+ },
174
+ hideLoadingUI: () => { },
175
+ loadingUIBackgroundColor: "",
176
+ loadingUIText: "",
177
+ };
149
178
  this._boundBuffersVertexArray = null;
179
+ this._clearAttachmentMask = _AllAttachmentsMask;
150
180
  this._currentDepthTest = _native.Engine.DEPTH_TEST_LEQUAL;
151
181
  this._depthTestEnabled = true;
152
182
  this._stencilTest = false;
@@ -167,6 +197,9 @@ export class ThinNativeEngine extends ThinEngine {
167
197
  this._depthWrite = true;
168
198
  // warning for non supported fill mode has already been displayed
169
199
  this._fillModeWarningDisplayed = false;
200
+ this._int2Scratch = new Int32Array(2);
201
+ this._int3Scratch = new Int32Array(3);
202
+ this._int4Scratch = new Int32Array(4);
170
203
  this._drawCalls = new PerfCounter();
171
204
  if (_native.Engine.PROTOCOL_VERSION !== ThinNativeEngine.PROTOCOL_VERSION) {
172
205
  throw new Error(`Protocol version mismatch: ${_native.Engine.PROTOCOL_VERSION} (Native) !== ${ThinNativeEngine.PROTOCOL_VERSION} (JS)`);
@@ -241,12 +274,12 @@ export class ThinNativeEngine extends ThinEngine {
241
274
  canUseGLVertexID: true,
242
275
  supportComputeShaders: false,
243
276
  supportSRGBBuffers: true,
244
- supportTransformFeedbacks: false,
277
+ supportTransformFeedbacks: !!_native.Engine.COMMAND_DRAWTRANSFORMFEEDBACK,
245
278
  textureMaxLevel: false,
246
279
  texture2DArrayMaxLayerCount: _native.Engine.CAPS_LIMITS_MAX_TEXTURE_LAYERS,
247
280
  disableMorphTargetTexture: false,
248
281
  parallelShaderCompile: { COMPLETION_STATUS_KHR: 0 },
249
- textureNorm16: false,
282
+ textureNorm16: true,
250
283
  blendParametersPerTarget: false,
251
284
  dualSourceBlending: false,
252
285
  supportReadWriteStorageTextures: false,
@@ -254,13 +287,23 @@ export class ThinNativeEngine extends ThinEngine {
254
287
  this._features = {
255
288
  forceBitmapOverHTMLImageElement: true,
256
289
  supportRenderAndCopyToLodForFloatTextures: false,
257
- supportDepthStencilTexture: false,
258
- supportShadowSamplers: false,
290
+ // Native can attach an explicit depth/stencil texture to a render target (see
291
+ // _createDepthStencilTexture) and sample it afterwards. Hardware comparison samplers are
292
+ // available through bgfx BGFX_SAMPLER_COMPARE_* (set via updateTextureComparisonFunction),
293
+ // which is what ShadowGenerator needs for FILTER_PCF / FILTER_PCSS (incl. cascaded shadows).
294
+ // Both are optional runtime extensions, so only advertise them when the runtime provides them.
295
+ supportDepthStencilTexture: !!this._engine.createMultiFrameBuffer,
296
+ supportShadowSamplers: !!this._engine.createMultiFrameBuffer && !!this._engine.setTextureComparisonFunction,
259
297
  uniformBufferHardCheckMatrix: false,
260
- allowTexturePrefiltering: false,
298
+ // Native supports GPU cube prefiltering (HDRFiltering / HDRIrradianceFiltering): the render path
299
+ // binds a specific cube-face + mip via bindFramebuffer(faceIndex, lodLevel) and convolves the
300
+ // environment per-roughness. Required so OpenPBR/PBR IBL scenes get real prefiltered radiance
301
+ // (and irradiance) instead of black/energy-lossy CPU-SH fallbacks.
302
+ allowTexturePrefiltering: true,
261
303
  trackUbosInFrame: false,
262
304
  checkUbosContentBeforeUpload: false,
263
- supportCSM: false,
305
+ // Cascaded shadow maps render into 2D-array render targets, one framebuffer per layer.
306
+ supportCSM: true,
264
307
  basisNeedsPOT: false,
265
308
  support3DTextures: false,
266
309
  needTypeSuffixInShaderConstants: false,
@@ -275,7 +318,7 @@ export class ThinNativeEngine extends ThinEngine {
275
318
  needShaderCodeInlining: true,
276
319
  needToAlwaysBindUniformBuffers: false,
277
320
  supportRenderPasses: true,
278
- supportSpriteInstancing: false,
321
+ supportSpriteInstancing: true,
279
322
  forceVertexBufferStrideAndOffsetMultiple4Bytes: true,
280
323
  _checkNonFloatVertexBuffersDontRecreatePipelineContext: false,
281
324
  };
@@ -413,6 +456,12 @@ export class ThinNativeEngine extends ThinEngine {
413
456
  }
414
457
  _restoreEngineAfterContextLost() {
415
458
  this._clearEmptyResources();
459
+ // The frame graph framebuffers of the lost context are invalid: forget their bookkeeping (without deleting
460
+ // the stale native handles) so each reset does not leave another generation of entries behind.
461
+ for (const shared of this._frameGraphFramebufferRefCount.values()) {
462
+ shared.hardwareTexture._frameGraphFramebuffer = null;
463
+ }
464
+ this._frameGraphFramebufferRefCount.clear();
416
465
  const depthTest = this._depthCullingState.depthTest; // backup those values because the call to initEngine / wipeCaches will reset them
417
466
  const depthFunc = this._depthCullingState.depthFunc;
418
467
  const depthMask = this._depthCullingState.depthMask;
@@ -441,6 +490,9 @@ export class ThinNativeEngine extends ThinEngine {
441
490
  this._commandBufferEncoder.finishEncodingCommand();
442
491
  }
443
492
  this._currentFramebuffer = framebuffer;
493
+ // drawBuffers state is per-framebuffer on WebGL, so a framebuffer switch resets the
494
+ // attachment selection back to "every attachment".
495
+ this._clearAttachmentMask = _AllAttachmentsMask;
444
496
  }
445
497
  }
446
498
  /**
@@ -462,16 +514,24 @@ export class ThinNativeEngine extends ThinEngine {
462
514
  this._depthCullingState.depthFunc = 518;
463
515
  this.setDepthFunction(518);
464
516
  }
465
- this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_CLEAR);
466
- this._commandBufferEncoder.encodeCommandArgAsUInt32(backBuffer && color ? 1 : 0);
517
+ const clear2 = _native.Engine.COMMAND_CLEAR2;
518
+ this._commandBufferEncoder.startEncodingCommand(clear2 ?? _native.Engine.COMMAND_CLEAR);
519
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(backBuffer && color && this._clearAttachmentMask !== 0 ? 1 : 0);
467
520
  this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.r : 0);
468
521
  this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.g : 0);
469
522
  this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.b : 0);
470
- this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.a : 1);
523
+ // Playgrounds often assign Color3 to scene.clearColor; Color3 has no .a, so
524
+ // `color.a` is undefined and was encoded as 0 → transparent clear. PNG
525
+ // screenshots then show black sky on dark HTML backgrounds while RGB still
526
+ // matches (pixel compare ignores alpha). Default missing alpha to opaque.
527
+ this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? (color.a ?? 1) : 1);
471
528
  this._commandBufferEncoder.encodeCommandArgAsUInt32(depth ? 1 : 0);
472
529
  this._commandBufferEncoder.encodeCommandArgAsFloat32(depth && this.useReverseDepthBuffer ? 0 : 1);
473
530
  this._commandBufferEncoder.encodeCommandArgAsUInt32(stencil ? 1 : 0);
474
531
  this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilClearValue);
532
+ if (clear2) {
533
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(this._clearAttachmentMask);
534
+ }
475
535
  this._commandBufferEncoder.finishEncodingCommand();
476
536
  }
477
537
  createIndexBuffer(indices, updateable, _label) {
@@ -545,6 +605,8 @@ export class ThinNativeEngine extends ThinEngine {
545
605
  this._commandBufferEncoder.encodeCommandArgAsNativeData(vertexArray);
546
606
  this._commandBufferEncoder.finishEncodingCommand();
547
607
  }
608
+ /** @internal Native vertex arrays capture their buffers when recorded; there is no bound array buffer. */
609
+ bindArrayBuffer(_buffer) { }
548
610
  releaseVertexArrayObject(vertexArray) {
549
611
  this._deleteVertexArray(vertexArray);
550
612
  }
@@ -560,12 +622,15 @@ export class ThinNativeEngine extends ThinEngine {
560
622
  return this._engine.getAttributes(nativePipelineContext.program, remappedAttributesNames);
561
623
  }
562
624
  /**
563
- * Triangle Fan and Line Loop are not supported by modern rendering API
625
+ * Check whether the runtime can expand primitive modes unavailable in modern rendering APIs.
564
626
  * @param fillMode defines the primitive to use
565
627
  * @returns true if supported
566
628
  */
567
629
  _checkSupportedFillMode(fillMode) {
568
630
  if (fillMode == 5 || fillMode == 8) {
631
+ if (this._engine.supportsPrimitiveModeExpansion) {
632
+ return true;
633
+ }
569
634
  if (!this._fillModeWarningDisplayed) {
570
635
  Logger.Warn("Line Loop and Triangle Fan are not supported fill modes with Babylon Native. Elements with these fill mode will not be visible.");
571
636
  this._fillModeWarningDisplayed = true;
@@ -611,6 +676,20 @@ export class ThinNativeEngine extends ThinEngine {
611
676
  * @param instancesCount defines the number of instances to draw (if instantiation is enabled)
612
677
  */
613
678
  drawArraysType(fillMode, verticesStart, verticesCount, instancesCount) {
679
+ if (this._transformFeedbackActive) {
680
+ // The native engine runs the transform feedback program over the bound vertex array
681
+ // and writes the captured varyings into the bound feedback buffer.
682
+ const target = this._transformFeedbackBuffer?.nativeVertexBuffer;
683
+ if (!target) {
684
+ throw new Error("Transform feedback requires a bound native vertex buffer.");
685
+ }
686
+ this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWTRANSFORMFEEDBACK);
687
+ this._commandBufferEncoder.encodeCommandArgAsNativeData(target);
688
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesStart);
689
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesCount);
690
+ this._commandBufferEncoder.finishEncodingCommand();
691
+ return;
692
+ }
614
693
  if (!this._checkSupportedFillMode(fillMode)) {
615
694
  return;
616
695
  }
@@ -632,6 +711,41 @@ export class ThinNativeEngine extends ThinEngine {
632
711
  }
633
712
  this._commandBufferEncoder.finishEncodingCommand();
634
713
  }
714
+ /** @internal Transform feedback objects are implicit on native; state lives on the engine. */
715
+ createTransformFeedback() {
716
+ return {};
717
+ }
718
+ /** @internal */
719
+ deleteTransformFeedback(_value) { }
720
+ /** @internal */
721
+ bindTransformFeedback(_value) { }
722
+ /** @internal */
723
+ bindTransformFeedbackBuffer(value) {
724
+ this._transformFeedbackBuffer = value;
725
+ }
726
+ /** @internal Varyings are passed to the native program when it is created. */
727
+ setTranformFeedbackVaryings(_program, _value) { }
728
+ /** @internal */
729
+ beginTransformFeedback(_usePoints = true) {
730
+ this._transformFeedbackActive = true;
731
+ }
732
+ /** @internal */
733
+ endTransformFeedback() {
734
+ this._transformFeedbackActive = false;
735
+ }
736
+ /** @internal */
737
+ readTransformFeedbackBuffer(target) {
738
+ const buffer = this._transformFeedbackBuffer?.nativeVertexBuffer;
739
+ if (!buffer) {
740
+ throw new Error("Reading transform feedback requires a bound native vertex buffer.");
741
+ }
742
+ if (!this._engine.readTransformFeedbackBuffer) {
743
+ throw new Error("This native engine build does not support reading transform feedback buffers.");
744
+ }
745
+ this._engine.readTransformFeedbackBuffer(buffer, target.buffer, target.byteOffset, target.byteLength);
746
+ }
747
+ /** @internal Transform feedback draws never rasterize on native. */
748
+ setRasterizerState(_value) { }
635
749
  createPipelineContext(shaderProcessingContext) {
636
750
  const isAsync = !!this._caps.parallelShaderCompile;
637
751
  return new NativePipelineContext(this, isAsync, shaderProcessingContext);
@@ -647,19 +761,22 @@ export class ThinNativeEngine extends ThinEngine {
647
761
  * @internal
648
762
  */
649
763
  // eslint-disable-next-line no-restricted-syntax
650
- _preparePipelineContextAsync(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, _rawVertexSourceCode, _rawFragmentSourceCode, _rebuildRebind, defines, _transformFeedbackVaryings, _key, onReady) {
764
+ _preparePipelineContextAsync(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, _rawVertexSourceCode, _rawFragmentSourceCode, _rebuildRebind, defines, transformFeedbackVaryings, _key, onReady) {
651
765
  if (createAsRaw) {
652
766
  this.createRawShaderProgram();
653
767
  }
654
768
  else {
655
- this.createShaderProgram(pipelineContext, vertexSourceCode, fragmentSourceCode, defines);
769
+ this.createShaderProgram(pipelineContext, vertexSourceCode, fragmentSourceCode, defines, undefined, transformFeedbackVaryings);
656
770
  }
657
771
  onReady();
658
772
  }
659
773
  /**
660
774
  * @internal
661
775
  */
662
- _getShaderProcessingContext(_shaderLanguage) {
776
+ _getShaderProcessingContext(shaderLanguage) {
777
+ if (shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
778
+ throw new Error("NativeEngine does not support WGSL graphics shaders.");
779
+ }
663
780
  return new NativeShaderProcessingContext();
664
781
  }
665
782
  /**
@@ -686,7 +803,11 @@ export class ThinNativeEngine extends ThinEngine {
686
803
  createRawShaderProgram() {
687
804
  throw new Error("Not Supported");
688
805
  }
689
- createShaderProgram(pipelineContext, vertexCode, fragmentCode, defines) {
806
+ createShaderProgram(pipelineContext, vertexCode, fragmentCode, defines, _context, transformFeedbackVaryings = null) {
807
+ const varyings = transformFeedbackVaryings?.length ? transformFeedbackVaryings : undefined;
808
+ if (varyings && !_native.Engine.COMMAND_DRAWTRANSFORMFEEDBACK) {
809
+ throw new Error("This native engine build does not support transform feedback.");
810
+ }
690
811
  const nativePipelineContext = pipelineContext;
691
812
  this.onBeforeShaderCompilationObservable.notifyObservers(this);
692
813
  const vertexInliner = new ShaderCodeInliner(vertexCode);
@@ -705,11 +826,11 @@ export class ThinNativeEngine extends ThinEngine {
705
826
  if (pipelineContext.isAsync) {
706
827
  nativePipelineContext.program = this._engine.createProgramAsync(vertexCode, fragmentCode, onSuccess, (error) => {
707
828
  nativePipelineContext.compilationError = error;
708
- });
829
+ }, varyings);
709
830
  }
710
831
  else {
711
832
  try {
712
- nativePipelineContext.program = this._engine.createProgram(vertexCode, fragmentCode);
833
+ nativePipelineContext.program = this._engine.createProgram(vertexCode, fragmentCode, varyings);
713
834
  onSuccess();
714
835
  }
715
836
  catch (e) {
@@ -906,23 +1027,19 @@ export class ThinNativeEngine extends ThinEngine {
906
1027
  // Unlike the WebGL engine, the native engine does not call applyStates() before
907
1028
  // a draw, so depth-test toggles made directly on engine.depthCullingState are
908
1029
  // flushed here to match the cross-engine contract.
1030
+ // Reconcile the depth compare function too: features that set it directly on
1031
+ // depthCullingState (e.g. reverse depth buffer -> GEQUAL) never go through
1032
+ // setDepthFunction, so push any divergence to the native side here. setDepthFunction
1033
+ // also re-encodes the depth-test enable, so no separate enable flush is needed after.
1034
+ const targetFunc = this._depthCullingState.depthFunc;
1035
+ if (targetFunc && targetFunc !== this.getDepthFunction()) {
1036
+ this.setDepthFunction(targetFunc);
1037
+ return;
1038
+ }
909
1039
  if (this._depthCullingState.depthTest !== this._depthTestEnabled) {
910
1040
  this._encodeDepthTest(this._depthCullingState.depthTest);
911
1041
  }
912
1042
  }
913
- applyStates() {
914
- // The base ThinEngine.applyStates() drives the WebGL context (this._gl) directly, which is null on
915
- // Native. Flush the depth-culling state through the native command path instead, so callers that
916
- // mutate engine.depthCullingState directly and then call applyStates() (e.g. the depth-peeling / OIT
917
- // renderer) take effect. Alpha and stencil state are applied on Native via their dedicated setters
918
- // (setAlphaMode / setStencil*), which encode their commands when called.
919
- const depthCullingState = this._depthCullingState;
920
- if (depthCullingState.depthFunc !== null && depthCullingState.depthFunc !== undefined) {
921
- this.setDepthFunction(depthCullingState.depthFunc);
922
- }
923
- this.setDepthBuffer(depthCullingState.depthTest);
924
- this.setDepthWrite(depthCullingState.depthMask);
925
- }
926
1043
  /**
927
1044
  * Gets a boolean indicating if depth writing is enabled
928
1045
  * @returns the current depth writing state
@@ -952,6 +1069,10 @@ export class ThinNativeEngine extends ThinEngine {
952
1069
  return null;
953
1070
  }
954
1071
  setDepthFunction(depthFunc) {
1072
+ // Keep the shared depth-culling state in sync (the base impl sets this) so the
1073
+ // per-draw reconcile in _flushDepthTestState treats material-driven changes as a
1074
+ // no-op and only pushes divergences that bypass this method (e.g. reverse depth).
1075
+ this._depthCullingState.depthFunc = depthFunc;
955
1076
  let nativeDepthFunc = 0;
956
1077
  switch (depthFunc) {
957
1078
  case 512:
@@ -994,6 +1115,13 @@ export class ThinNativeEngine extends ThinEngine {
994
1115
  */
995
1116
  setDepthWrite(enable) {
996
1117
  this._depthWrite = enable;
1118
+ // Keep depthCullingState.depthMask in sync. applyStates() reconciles native depth write
1119
+ // from depthMask; if it stays true after setDepthWrite(false), the next applyStates()
1120
+ // (e.g. during particle/material draws) silently turns WRITE_Z back on. Coplanar
1121
+ // triangle-strip particle quads then lose the second tri to DEPTH_LESS.
1122
+ if (this._depthCullingState) {
1123
+ this._depthCullingState.depthMask = enable;
1124
+ }
997
1125
  this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHWRITE);
998
1126
  this._commandBufferEncoder.encodeCommandArgAsUInt32(Number(enable));
999
1127
  this._commandBufferEncoder.finishEncodingCommand();
@@ -1015,17 +1143,49 @@ export class ThinNativeEngine extends ThinEngine {
1015
1143
  getColorWrite() {
1016
1144
  return this._colorWrite;
1017
1145
  }
1146
+ /**
1147
+ * Apply the currently pending engine states.
1148
+ *
1149
+ * The base ThinEngine.applyStates() flushes state by calling
1150
+ * this._depthCullingState.apply(this._gl) (and similar for alpha/stencil), but the
1151
+ * native engine has no WebGL context (_gl is undefined), so inheriting that path
1152
+ * throws "Cannot read properties of undefined (reading 'depthMask')". Callers such
1153
+ * as the depth-peeling (OIT) renderer mutate engine.depthCullingState directly and
1154
+ * then call applyStates() expecting the change to be flushed, so reconcile the shared
1155
+ * depth-culling state into the native command buffer here instead. Alpha and stencil
1156
+ * states are already encoded immediately on the native side (setAlphaMode /
1157
+ * setStencil* emit commands directly), so only the depth state needs reconciling.
1158
+ */
1159
+ applyStates() {
1160
+ // Depth test (the native command conflates enable + compare function).
1161
+ this._flushDepthTestState();
1162
+ // Depth write (depthMask).
1163
+ if (this._depthCullingState.depthMask !== this._depthWrite) {
1164
+ this.setDepthWrite(this._depthCullingState.depthMask);
1165
+ }
1166
+ }
1018
1167
  applyStencil() {
1019
- this._setStencil(this._stencilMask, getNativeStencilOpFail(this._stencilOpStencilFail), getNativeStencilDepthFail(this._stencilOpDepthFail), getNativeStencilDepthPass(this._stencilOpStencilDepthPass), getNativeStencilFunc(this._stencilFunc), this._stencilFuncRef);
1168
+ if (!this._stencilTest) {
1169
+ return;
1170
+ }
1171
+ this._setStencil(this._stencilMask, getNativeStencilOpFail(this._stencilOpStencilFail), getNativeStencilDepthFail(this._stencilOpDepthFail), getNativeStencilDepthPass(this._stencilOpStencilDepthPass), getNativeStencilFunc(this._stencilFunc), this._stencilFuncRef, this._stencilFuncMask);
1020
1172
  }
1021
- _setStencil(mask, stencilOpFail, depthOpFail, depthOpPass, func, ref) {
1022
- this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSTENCIL);
1173
+ _setStencil(mask, stencilOpFail, depthOpFail, depthOpPass, func, ref, funcMask = 0xff) {
1174
+ const setStencil2 = _native.Engine.COMMAND_SETSTENCIL2;
1175
+ this._commandBufferEncoder.startEncodingCommand(setStencil2 ?? _native.Engine.COMMAND_SETSTENCIL);
1023
1176
  this._commandBufferEncoder.encodeCommandArgAsUInt32(mask);
1024
1177
  this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilOpFail);
1025
1178
  this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpFail);
1026
1179
  this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpPass);
1027
1180
  this._commandBufferEncoder.encodeCommandArgAsUInt32(func);
1028
1181
  this._commandBufferEncoder.encodeCommandArgAsUInt32(ref);
1182
+ // Stencil function mask (gl.stencilFunc mask / BGFX_STENCIL_FUNC_RMASK). Required for
1183
+ // HighlightLayer, which compares only the glowing-mesh reference bits while ignoring
1184
+ // lower reserved bits. Without this, Native always used 0xFF and the outer/inner glow
1185
+ // stencil tests matched the wrong fragments.
1186
+ if (setStencil2) {
1187
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(funcMask & 0xff);
1188
+ }
1029
1189
  this._commandBufferEncoder.finishEncodingCommand();
1030
1190
  }
1031
1191
  /**
@@ -1088,11 +1248,12 @@ export class ThinNativeEngine extends ThinEngine {
1088
1248
  this.applyStencil();
1089
1249
  }
1090
1250
  /**
1091
- * Sets the current stencil mask
1092
- * @param mask defines the new stencil mask to use
1251
+ * Sets the current stencil function mask
1252
+ * @param mask defines the new stencil function mask to use
1093
1253
  */
1094
1254
  setStencilFunctionMask(mask) {
1095
1255
  this._stencilFuncMask = mask;
1256
+ this.applyStencil();
1096
1257
  }
1097
1258
  /**
1098
1259
  * Sets the stencil operation to use when stencil fails
@@ -1182,6 +1343,29 @@ export class ThinNativeEngine extends ThinEngine {
1182
1343
  }
1183
1344
  this._alphaMode[targetIndex] = mode;
1184
1345
  }
1346
+ /**
1347
+ * Sets the current alpha blend equation (ADD / SUB / MAX / MIN / ...).
1348
+ * Dual depth peeling (OIT) requires ALPHA_EQUATION_MAX so peel passes accumulate
1349
+ * farthest/nearest depths. Native previously only wired blend *factors*; equations
1350
+ * stayed at bgfx's default ADD and transparent peels never wrote useful depth.
1351
+ * @param equation one of undefined*
1352
+ * @param targetIndex MRT attachment index (Native applies equation globally; kept for API parity)
1353
+ */
1354
+ setAlphaEquation(equation, targetIndex = 0) {
1355
+ if (this._alphaEquation[targetIndex] === equation) {
1356
+ return;
1357
+ }
1358
+ this._alphaEquation[targetIndex] = equation;
1359
+ const command = _native.Engine.COMMAND_SETBLENDEQUATION;
1360
+ if (!command) {
1361
+ // Older Babylon Native binaries predating blend-equation support.
1362
+ return;
1363
+ }
1364
+ this._commandBufferEncoder.startEncodingCommand(command);
1365
+ // Pass the Babylon undefined* value; NativeEngine::SetBlendEquation maps it to bgfx.
1366
+ this._commandBufferEncoder.encodeCommandArgAsUInt32(equation);
1367
+ this._commandBufferEncoder.finishEncodingCommand();
1368
+ }
1185
1369
  setInt(uniform, int) {
1186
1370
  if (!uniform) {
1187
1371
  return false;
@@ -1192,6 +1376,36 @@ export class ThinNativeEngine extends ThinEngine {
1192
1376
  this._commandBufferEncoder.finishEncodingCommand();
1193
1377
  return true;
1194
1378
  }
1379
+ setInt2(uniform, x, y) {
1380
+ if (!uniform) {
1381
+ return false;
1382
+ }
1383
+ const values = this._int2Scratch;
1384
+ values[0] = x;
1385
+ values[1] = y;
1386
+ return this.setIntArray2(uniform, values);
1387
+ }
1388
+ setInt3(uniform, x, y, z) {
1389
+ if (!uniform) {
1390
+ return false;
1391
+ }
1392
+ const values = this._int3Scratch;
1393
+ values[0] = x;
1394
+ values[1] = y;
1395
+ values[2] = z;
1396
+ return this.setIntArray3(uniform, values);
1397
+ }
1398
+ setInt4(uniform, x, y, z, w) {
1399
+ if (!uniform) {
1400
+ return false;
1401
+ }
1402
+ const values = this._int4Scratch;
1403
+ values[0] = x;
1404
+ values[1] = y;
1405
+ values[2] = z;
1406
+ values[3] = w;
1407
+ return this.setIntArray4(uniform, values);
1408
+ }
1195
1409
  setIntArray(uniform, array) {
1196
1410
  if (!uniform) {
1197
1411
  return false;
@@ -1497,13 +1711,176 @@ export class ThinNativeEngine extends ThinEngine {
1497
1711
  this._internalTexturesCache.push(texture);
1498
1712
  return texture;
1499
1713
  }
1714
+ createRawTexture3D(data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = 0) {
1715
+ if (compression) {
1716
+ throw new Error(`NativeEngine.createRawTexture3D: compressed raw volume uploads are not supported (${compression}).`);
1717
+ }
1718
+ const texture = new InternalTexture(this, 10 /* InternalTextureSource.Raw3D */);
1719
+ texture.baseWidth = width;
1720
+ texture.baseHeight = height;
1721
+ texture.baseDepth = depth;
1722
+ texture.width = width;
1723
+ texture.height = height;
1724
+ texture.depth = depth;
1725
+ texture.format = format;
1726
+ texture.type = textureType;
1727
+ texture.generateMipMaps = generateMipMaps;
1728
+ texture.samplingMode = samplingMode;
1729
+ texture.is3D = true;
1730
+ texture.invertY = invertY;
1731
+ texture._compression = compression;
1732
+ if (!this._doNotHandleContextLost) {
1733
+ texture._bufferView = data;
1734
+ }
1735
+ if (texture._hardwareTexture) {
1736
+ const nativeTexture = texture._hardwareTexture.underlyingResource;
1737
+ this._engine.loadRawTexture3D(nativeTexture, data, width, height, depth, getNativeTextureFormat(format, textureType), generateMipMaps, invertY);
1738
+ const filter = getNativeSamplingMode(samplingMode);
1739
+ this._setTextureSampling(nativeTexture, filter);
1740
+ }
1741
+ texture.isReady = true;
1742
+ this._internalTexturesCache.push(texture);
1743
+ return texture;
1744
+ }
1745
+ updateRawTexture3D(texture, bufferView, format, invertY, compression = null, textureType = 0) {
1746
+ if (!texture) {
1747
+ return;
1748
+ }
1749
+ if (compression) {
1750
+ throw new Error(`NativeEngine.updateRawTexture3D: compressed raw volume uploads are not supported (${compression}).`);
1751
+ }
1752
+ texture.format = format;
1753
+ texture.type = textureType;
1754
+ texture.invertY = invertY;
1755
+ texture._compression = compression;
1756
+ if (!this._doNotHandleContextLost) {
1757
+ texture._bufferView = bufferView;
1758
+ }
1759
+ if (bufferView && texture._hardwareTexture) {
1760
+ const nativeTexture = texture._hardwareTexture.underlyingResource;
1761
+ this._engine.loadRawTexture3D(nativeTexture, bufferView, texture.width, texture.height, texture.depth, getNativeTextureFormat(format, textureType), texture.generateMipMaps, invertY);
1762
+ }
1763
+ texture.isReady = true;
1764
+ }
1500
1765
  updateRawTexture(texture, bufferView, format, invertY, compression = null, type = 0, useSRGBBuffer = false) {
1501
1766
  if (!texture) {
1502
1767
  return;
1503
1768
  }
1504
1769
  if (bufferView && texture._hardwareTexture) {
1505
1770
  const underlyingResource = texture._hardwareTexture.underlyingResource;
1506
- this._engine.loadRawTexture(underlyingResource, bufferView, texture.width, texture.height, getNativeTextureFormat(format, type), texture.generateMipMaps, texture.invertY);
1771
+ this._engine.loadRawTexture(underlyingResource, bufferView, texture.width, texture.height, getNativeTextureFormat(format, type), texture.generateMipMaps, texture.invertY, useSRGBBuffer);
1772
+ }
1773
+ texture.isReady = true;
1774
+ }
1775
+ /**
1776
+ * Creates a raw cube texture on the native engine.
1777
+ *
1778
+ * The WebGL implementation (engine.rawTexture) drives the whole upload through `this._gl`, which is null
1779
+ * on Native, so loading an HDR/`.env` cube via `createRawCubeTextureFromUrl` used to throw
1780
+ * `Cannot read properties of undefined (reading 'FLOAT')`. This override allocates a native cube texture
1781
+ * and uploads its faces through the bgfx `updateTextureData` path instead.
1782
+ *
1783
+ * Native has no 3-component float texture format, so an RGB float/half-float source (what HDRCubeTexture
1784
+ * requests) is allocated and uploaded as RGBA; the per-face RGB->RGBA expansion happens in
1785
+ * `updateRawCubeTexture`.
1786
+ * @param data defines the data used to create the texture (6 faces, +X +Y +Z -X -Y -Z) or null
1787
+ * @param size defines the size of the textures (each face is size x size)
1788
+ * @param format defines the format of the data
1789
+ * @param type defines the type of the data
1790
+ * @param generateMipMaps defines if the engine should generate the mip levels
1791
+ * @param invertY defines if data must be stored with Y axis inverted
1792
+ * @param samplingMode defines the required sampling mode (like Texture.NEAREST_SAMPLINGMODE)
1793
+ * @param compression defines the compression used (null by default)
1794
+ * @returns the cube texture as an InternalTexture
1795
+ */
1796
+ createRawCubeTexture(data, size, format, type, generateMipMaps, invertY, samplingMode, compression = null) {
1797
+ if (compression) {
1798
+ throw new Error(`NativeEngine.createRawCubeTexture: compressed raw cube uploads are not supported (${compression}).`);
1799
+ }
1800
+ const texture = new InternalTexture(this, 8 /* InternalTextureSource.CubeRaw */);
1801
+ texture.isCube = true;
1802
+ texture.format = format;
1803
+ texture.type = type;
1804
+ texture.width = size;
1805
+ texture.height = size;
1806
+ texture.baseWidth = size;
1807
+ texture.baseHeight = size;
1808
+ texture.invertY = invertY;
1809
+ texture._compression = compression;
1810
+ // Match the WebGL raw-cube path and createRenderTargetCubeTexture: float/half-float formats that the
1811
+ // platform cannot linearly filter fall back to NEAREST and drop mip generation.
1812
+ if (type === 1 && !this._caps.textureFloatLinearFiltering) {
1813
+ generateMipMaps = false;
1814
+ samplingMode = 1;
1815
+ }
1816
+ else if (type === 2 && !this._caps.textureHalfFloatLinearFiltering) {
1817
+ generateMipMaps = false;
1818
+ samplingMode = 1;
1819
+ }
1820
+ texture.generateMipMaps = generateMipMaps;
1821
+ texture.samplingMode = samplingMode;
1822
+ const nativeTexture = texture._hardwareTexture.underlyingResource;
1823
+ this._engine.initializeTexture(nativeTexture, size, size, generateMipMaps, getNativeTextureFormat(format, type),
1824
+ /*renderTarget*/ false,
1825
+ /*srgb*/ false,
1826
+ /*samples*/ 1,
1827
+ /*isCube*/ true);
1828
+ this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));
1829
+ if (data) {
1830
+ this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
1831
+ }
1832
+ else {
1833
+ texture.isReady = true;
1834
+ }
1835
+ // Like WebGL, the cache registration is owned by the caller (e.g. createRawCubeTextureFromUrl).
1836
+ return texture;
1837
+ }
1838
+ /**
1839
+ * Updates a raw cube texture on the native engine.
1840
+ * @param texture defines the texture to update
1841
+ * @param data defines the data to store (6 faces, +X +Y +Z -X -Y -Z)
1842
+ * @param format defines the data format
1843
+ * @param type defines the type of the data
1844
+ * @param invertY defines if data must be stored with Y axis inverted
1845
+ * @param compression defines the compression used (null by default)
1846
+ * @param level defines which mip level of the texture to update (0 by default)
1847
+ */
1848
+ updateRawCubeTexture(texture, data, format, type, invertY, compression = null, level = 0) {
1849
+ if (compression) {
1850
+ throw new Error(`NativeEngine.updateRawCubeTexture: compressed raw cube uploads are not supported (${compression}).`);
1851
+ }
1852
+ const descriptorChanged = texture.format !== format || texture.type !== type;
1853
+ if (descriptorChanged && level !== 0) {
1854
+ throw new Error("NativeEngine.updateRawCubeTexture: changing the format or type is only supported when updating level 0.");
1855
+ }
1856
+ const nativeTexture = texture._hardwareTexture?.underlyingResource;
1857
+ const nativeTextureFormat = descriptorChanged ? getNativeTextureFormat(format, type) : undefined;
1858
+ if (descriptorChanged && nativeTexture) {
1859
+ this._engine.initializeTexture(nativeTexture, texture.width, texture.height, texture.generateMipMaps, nativeTextureFormat,
1860
+ /*renderTarget*/ false, texture._useSRGBBuffer,
1861
+ /*samples*/ 1,
1862
+ /*isCube*/ true);
1863
+ this._setTextureSampling(nativeTexture, getNativeSamplingMode(texture.samplingMode));
1864
+ }
1865
+ // NativeEngine.updateTextureData honors invertY directly on cubes (no D3D origin
1866
+ // compensation). That matches WebGL: HDRCubeTexture uploads with invertY=false and
1867
+ // UNPACK_FLIP_Y_WEBGL=false. Forcing invertY here double-flipped float/half faces and
1868
+ // made the -Y cubemap a hard brown cap on OpenPBR IBL (513/514). Byte RGBD .env
1869
+ // uploads already pass invertY=true and are unchanged.
1870
+ texture.format = format;
1871
+ texture.type = type;
1872
+ texture.invertY = invertY;
1873
+ texture._compression = compression;
1874
+ if (level === 0 && !this._doNotHandleContextLost) {
1875
+ texture._bufferViewArray = data;
1876
+ }
1877
+ // Data are in +X +Y +Z -X -Y -Z order, which matches the bgfx cube sides. Like gl.generateMipmap in the
1878
+ // WebGL path, Native rebuilds the rest of each face's mip chain from a full level 0 upload.
1879
+ const width = Math.max(1, texture.width >> level);
1880
+ const height = Math.max(1, texture.height >> level);
1881
+ const generateMipMaps = level === 0 && texture.generateMipMaps;
1882
+ for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
1883
+ this.updateTextureData(texture, data[faceIndex], 0, 0, width, height, faceIndex, level, generateMipMaps);
1507
1884
  }
1508
1885
  texture.isReady = true;
1509
1886
  }
@@ -1547,8 +1924,14 @@ export class ThinNativeEngine extends ThinEngine {
1547
1924
  // some formats are already supported by bimg, no need to try to load them with JS
1548
1925
  // leaving TextureLoader extension check for future use
1549
1926
  let loaderPromise = null;
1550
- if (extension.endsWith(".basis") || extension.endsWith(".ktx") || extension.endsWith(".ktx2") || mimeType === "image/ktx" || mimeType === "image/ktx2") {
1551
- loaderPromise = AbstractEngine.GetCompatibleTextureLoader(extension);
1927
+ if (extension.endsWith(".ies")) {
1928
+ // The UMD/global build (used by Babylon Native) stubs dynamic import(), so
1929
+ // AbstractEngine.GetCompatibleTextureLoader cannot resolve the loader module. Instantiate the
1930
+ // statically-bundled IES loader directly instead.
1931
+ loaderPromise = Promise.resolve(new _IESTextureLoader());
1932
+ }
1933
+ else if (extension.endsWith(".basis") || extension.endsWith(".ktx") || extension.endsWith(".ktx2") || mimeType === "image/ktx" || mimeType === "image/ktx2") {
1934
+ loaderPromise = AbstractEngine.GetCompatibleTextureLoader(extension, mimeType);
1552
1935
  }
1553
1936
  if (scene) {
1554
1937
  scene.addPendingData(texture);
@@ -1570,6 +1953,7 @@ export class ThinNativeEngine extends ThinEngine {
1570
1953
  this._internalTexturesCache.push(texture);
1571
1954
  }
1572
1955
  const onInternalError = (message, exception) => {
1956
+ texture.isReady = false;
1573
1957
  if (scene) {
1574
1958
  scene.removePendingData(texture);
1575
1959
  }
@@ -1594,7 +1978,78 @@ export class ThinNativeEngine extends ThinEngine {
1594
1978
  };
1595
1979
  // processing for non-image formats
1596
1980
  if (loaderPromise) {
1597
- throw new Error("Loading textures from IInternalTextureLoader not yet implemented.");
1981
+ // These formats (e.g. .ies) are decoded on the JS side by an IInternalTextureLoader and then
1982
+ // uploaded to the native texture as raw pixel data. bimg cannot decode them directly.
1983
+ const texLoaderPromise = loaderPromise;
1984
+ const callbackAsync = async (data) => {
1985
+ const loader = await texLoaderPromise;
1986
+ loader.loadData(data, texture, (width, height, loadMipmap, isCompressed, done, loadFailed) => {
1987
+ if (loadFailed) {
1988
+ onInternalError("TextureLoader failed to load data");
1989
+ return;
1990
+ }
1991
+ const baseWidth = texture.baseWidth || width;
1992
+ const baseHeight = texture.baseHeight || height;
1993
+ texture.baseWidth = baseWidth;
1994
+ texture.baseHeight = baseHeight;
1995
+ texture.width = baseWidth;
1996
+ texture.height = baseHeight;
1997
+ // The loader-supplied done() performs the actual GPU upload (e.g. via
1998
+ // _uploadDataToTextureDirectly), so texture dimensions must be set beforehand.
1999
+ try {
2000
+ done();
2001
+ }
2002
+ finally {
2003
+ // Some loaders temporarily publish each mip's dimensions through width/height.
2004
+ // Restore the level-zero dimensions even when a later mip upload fails.
2005
+ texture.baseWidth = baseWidth;
2006
+ texture.baseHeight = baseHeight;
2007
+ texture.width = baseWidth;
2008
+ texture.height = baseHeight;
2009
+ }
2010
+ if (texture._hardwareTexture) {
2011
+ const filter = getNativeSamplingMode(samplingMode);
2012
+ this._setTextureSampling(texture._hardwareTexture.underlyingResource, filter);
2013
+ }
2014
+ texture.isReady = true;
2015
+ if (scene) {
2016
+ scene.removePendingData(texture);
2017
+ }
2018
+ texture.onLoadedObservable.notifyObservers(texture);
2019
+ texture.onLoadedObservable.clear();
2020
+ }, loaderOptions);
2021
+ };
2022
+ if (buffer) {
2023
+ const processBufferAsync = async (data) => {
2024
+ try {
2025
+ await callbackAsync(data);
2026
+ }
2027
+ catch (reason) {
2028
+ onInternalError("Failed to parse texture data", reason);
2029
+ }
2030
+ };
2031
+ if (buffer instanceof ArrayBuffer) {
2032
+ void processBufferAsync(new Uint8Array(buffer));
2033
+ }
2034
+ else if (ArrayBuffer.isView(buffer)) {
2035
+ void processBufferAsync(buffer);
2036
+ }
2037
+ else {
2038
+ onInternalError("Unable to load: only ArrayBuffer or ArrayBufferView is supported");
2039
+ }
2040
+ }
2041
+ else {
2042
+ this._loadFile(url, async (data) => {
2043
+ try {
2044
+ await callbackAsync(new Uint8Array(data));
2045
+ }
2046
+ catch (reason) {
2047
+ onInternalError("Failed to parse texture data", reason);
2048
+ }
2049
+ }, undefined, undefined, true, (request, exception) => {
2050
+ onInternalError("Unable to load " + (request ? request.responseURL : url), exception);
2051
+ });
2052
+ }
1598
2053
  }
1599
2054
  else {
1600
2055
  const onload = (data) => {
@@ -1740,16 +2195,51 @@ export class ThinNativeEngine extends ThinEngine {
1740
2195
  continue;
1741
2196
  }
1742
2197
  const nativeRTWrapper = rtWrapper;
2198
+ if (nativeRTWrapper._framebuffers && !rtWrapper.isCube) {
2199
+ // Wrapped 2D-array color attachment with one framebuffer per layer: rebuild every layer
2200
+ // against the new handle. The _framebuffers setter releases the stale handles.
2201
+ const { generateDepth, generateStencil } = this._getLayerFramebufferDepthStencil(rtWrapper);
2202
+ nativeRTWrapper._framebuffers = this._createLayerFramebuffers(texture, rtWrapper.width, rtWrapper.height, nativeRTWrapper._framebuffers.length, generateStencil, generateDepth, rtWrapper.samples ?? 1, rtWrapper.texture?.generateMipMaps);
2203
+ continue;
2204
+ }
1743
2205
  // NativeRenderTargetWrapper._framebuffer setter releases the old framebuffer before assigning,
1744
2206
  // so no manual _releaseFramebufferObjects call is needed (and would double-delete the handle).
1745
2207
  nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(texture, rtWrapper.width, rtWrapper.height, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer, rtWrapper.samples ?? 1);
1746
2208
  }
1747
2209
  }
2210
+ /**
2211
+ * Creates a depth/stencil texture for a render target wrapper.
2212
+ * @param size The size of the depth texture
2213
+ * @param options The options defining the depth texture
2214
+ * @param rtWrapper The render target wrapper the depth texture belongs to
2215
+ * @returns The depth/stencil texture
2216
+ */
2217
+ createDepthStencilTexture(size, options, rtWrapper) {
2218
+ // AbstractEngine routes cube render targets (point-light shadow maps) to _createDepthStencilCubeTexture,
2219
+ // which only exists on the WebGL engine and drives gl.TEXTURE_CUBE_MAP directly. Native already handles
2220
+ // cube and 2D-array targets in _createDepthStencilTexture through the wrapper's per-face framebuffers,
2221
+ // so route every request there instead of throwing on an undefined GL context.
2222
+ return this._createDepthStencilTexture(size, options, rtWrapper);
2223
+ }
1748
2224
  _createDepthStencilTexture(size, options, rtWrapper) {
1749
2225
  // TODO: handle other options?
1750
- const generateStencil = options.generateStencil || false;
2226
+ const requestedFormat = options.depthTextureFormat;
2227
+ // Native allocates either D32F (depth only) or D24S8 (depth + stencil). Requested depth formats map onto
2228
+ // whichever of those matches their stencil aspect, and texture.format records what was actually allocated.
2229
+ const requestedStencil = requestedFormat === 13 ||
2230
+ requestedFormat === 17 ||
2231
+ requestedFormat === 18;
2232
+ if (requestedFormat !== undefined &&
2233
+ !requestedStencil &&
2234
+ requestedFormat !== 15 &&
2235
+ requestedFormat !== 16 &&
2236
+ requestedFormat !== 14) {
2237
+ throw new Error(`NativeEngine: depth texture format ${requestedFormat} is not supported.`);
2238
+ }
2239
+ const generateStencil = !!options.generateStencil || requestedStencil;
1751
2240
  const samples = options.samples || 1;
1752
2241
  const nativeRTWrapper = rtWrapper;
2242
+ this._assertDepthAttachmentCompatible(nativeRTWrapper, samples);
1753
2243
  const texture = new InternalTexture(this, 12 /* InternalTextureSource.DepthStencil */);
1754
2244
  const width = size.width ?? size;
1755
2245
  const height = size.height ?? size;
@@ -1765,27 +2255,367 @@ export class ThinNativeEngine extends ThinEngine {
1765
2255
  texture.width = width;
1766
2256
  texture.height = height;
1767
2257
  texture.is2DArray = layers > 0;
2258
+ texture.isCube = !!options.isCube;
1768
2259
  texture.depth = layers || depth;
1769
2260
  texture.isReady = true;
1770
2261
  texture.samples = samples;
1771
2262
  texture.generateMipMaps = false;
1772
2263
  texture.samplingMode = options.bilinearFiltering ? 2 : 1;
1773
2264
  texture.type = 0;
2265
+ texture.format = generateStencil ? 13 : 14;
1774
2266
  texture._comparisonFunction = options.comparisonFunction ?? 0;
1775
- const framebuffer = this._engine.createFrameBuffer(texture._hardwareTexture.underlyingResource, width, height, generateStencil, true, samples);
1776
- nativeRTWrapper._framebufferDepthStencil = framebuffer;
2267
+ nativeRTWrapper._depthStencilTextureWithStencil = generateStencil;
2268
+ this._attachDepthStencilTexture(nativeRTWrapper, texture, width, height, generateStencil, samples);
1777
2269
  return texture;
1778
2270
  }
2271
+ /**
2272
+ * Builds the framebuffer(s) of a render target so they carry its sampleable depth/stencil texture. The depth
2273
+ * texture's native handle must not be allocated yet: the first framebuffer creation allocates it.
2274
+ * @internal
2275
+ */
2276
+ _attachDepthStencilTexture(nativeRTWrapper, texture, width, height, generateStencil, samples) {
2277
+ if (nativeRTWrapper.is3D) {
2278
+ this._assertVolumeDepthAttachmentSupported(texture.samples, samples);
2279
+ // Volume slices bind lazily built framebuffers; drop cached slices so the next bind attaches the new depth.
2280
+ nativeRTWrapper._releaseLayerFramebuffers();
2281
+ return;
2282
+ }
2283
+ if (nativeRTWrapper._framebuffers) {
2284
+ // Layered (2D array) or cube render target: the color framebuffers were created one-per-layer
2285
+ // (see createRenderTargetTexture / createRenderTargetCubeTexture). When the color RTT was built
2286
+ // without a depth buffer (e.g. cascaded shadow maps create the RTT with generateDepthBuffer=false
2287
+ // and then call createDepthStencilTexture), rebuild each per-layer framebuffer so it carries a
2288
+ // *shared sampleable* depth/stencil array attachment. PCF binds the whole array as
2289
+ // sampler2DArrayShadow; each cascade layer framebuffer writes into its own depth slice.
2290
+ // A single shared _framebufferDepthStencil would be ignored because bindFramebuffer selects
2291
+ // _framebuffers[layer] first.
2292
+ const colorTexture = nativeRTWrapper.texture;
2293
+ if (colorTexture && colorTexture._hardwareTexture) {
2294
+ const nativeColor = colorTexture._hardwareTexture.underlyingResource;
2295
+ const nativeDepth = texture._hardwareTexture.underlyingResource;
2296
+ const layerCount = nativeRTWrapper._framebuffers.length;
2297
+ const framebuffers = [];
2298
+ for (let layer = 0; layer < layerCount; layer++) {
2299
+ // First call creates the multi-layer sampleable depth and aliases it into nativeDepth;
2300
+ // subsequent calls borrow the same depth array and attach the matching layer.
2301
+ framebuffers.push(this._createNativeMultiFrameBuffer([nativeColor], width, height, generateStencil, true, samples, [layer], nativeDepth));
2302
+ }
2303
+ // The _framebuffers setter releases the previous handles.
2304
+ nativeRTWrapper._framebuffers = framebuffers;
2305
+ this._setTextureSampling(nativeDepth, getNativeSamplingMode(texture.samplingMode));
2306
+ this.updateTextureComparisonFunction(texture, texture._comparisonFunction);
2307
+ }
2308
+ return;
2309
+ }
2310
+ // Plain 2D render target: build a framebuffer that carries BOTH the render target's color attachment
2311
+ // and the standalone depth/stencil texture. bindFramebuffer prefers _framebufferDepthStencil over
2312
+ // _framebuffer, so attaching depth alone would silently drop the color target -- shadow maps would
2313
+ // render their packed depth into nothing. The depth texture's bgfx handle is still invalid at this
2314
+ // point, which is how CreateFrameBufferImpl recognizes a request for a readable depth attachment that
2315
+ // it aliases back into the supplied texture so it can be sampled afterwards.
2316
+ const colorResources = [];
2317
+ for (const colorTexture of nativeRTWrapper.textures ?? []) {
2318
+ const colorResource = colorTexture?._hardwareTexture?.underlyingResource;
2319
+ if (colorResource) {
2320
+ colorResources.push(colorResource);
2321
+ }
2322
+ }
2323
+ const nativeDepth = texture._hardwareTexture.underlyingResource;
2324
+ const framebuffer = this._createNativeMultiFrameBuffer(colorResources, width, height, generateStencil, true, samples, undefined, nativeDepth);
2325
+ nativeRTWrapper._framebufferDepthStencil = framebuffer;
2326
+ this._setTextureSampling(nativeDepth, getNativeSamplingMode(texture.samplingMode));
2327
+ this.updateTextureComparisonFunction(texture, texture._comparisonFunction);
2328
+ }
2329
+ // Reallocates a render target's sampleable depth/stencil texture with a new sample count and reattaches it to
2330
+ // the (already rebuilt) color framebuffer(s), so rendering and later depth sampling keep using the same resource.
2331
+ _recreateDepthStencilTexture(nativeRTWrapper, samples) {
2332
+ const depthTexture = nativeRTWrapper._depthStencilTexture;
2333
+ if (!depthTexture?._hardwareTexture) {
2334
+ return;
2335
+ }
2336
+ const previous = depthTexture._hardwareTexture;
2337
+ depthTexture._hardwareTexture = this._createHardwareTexture();
2338
+ depthTexture.samples = samples;
2339
+ if (nativeRTWrapper.isMulti) {
2340
+ nativeRTWrapper._framebufferDepthStencil = null;
2341
+ }
2342
+ else {
2343
+ this._attachDepthStencilTexture(nativeRTWrapper, depthTexture, depthTexture.width, depthTexture.height, nativeRTWrapper._depthStencilTextureWithStencil, samples);
2344
+ }
2345
+ previous.release();
2346
+ }
2347
+ /**
2348
+ * Returns whether a render-target texture was created with mip storage, independently of generateMipMaps.
2349
+ * @internal
2350
+ */
2351
+ _hasMipStorage(texture) {
2352
+ return _TexturesWithMipStorage.has(texture) || texture.generateMipMaps;
2353
+ }
2354
+ _setHasMipStorage(texture, hasMipStorage) {
2355
+ if (hasMipStorage) {
2356
+ _TexturesWithMipStorage.add(texture);
2357
+ }
2358
+ else {
2359
+ _TexturesWithMipStorage.delete(texture);
2360
+ }
2361
+ }
2362
+ /**
2363
+ * Ensures a cloned render-target texture retains allocation-only mip storage.
2364
+ * @internal
2365
+ */
2366
+ _ensureRenderTargetMipStorage(texture) {
2367
+ if (this._hasMipStorage(texture)) {
2368
+ return false;
2369
+ }
2370
+ this._setHasMipStorage(texture, true);
2371
+ if (!texture._hardwareTexture) {
2372
+ return false;
2373
+ }
2374
+ this._engine.initializeTexture(texture._hardwareTexture.underlyingResource, texture.baseWidth, texture.baseHeight, this._getRenderTargetHasMips(true, texture.samples), getNativeTextureFormat(texture.format, texture.type),
2375
+ /*renderTarget*/ true, texture._useSRGBBuffer, texture.samples, texture.isCube, texture.is2DArray || texture.is3D ? texture.depth : 0, texture.is3D);
2376
+ return true;
2377
+ }
2378
+ /**
2379
+ * Rebuilds native framebuffer objects after a wrapper's explicit depth attachment changes.
2380
+ * @internal
2381
+ */
2382
+ _rebuildRenderTargetFramebuffersAfterDepthChange(nativeRTWrapper) {
2383
+ nativeRTWrapper._releaseLayerFramebuffers();
2384
+ nativeRTWrapper._framebufferDepthStencil = null;
2385
+ if (nativeRTWrapper._usesLayeredFramebuffer) {
2386
+ nativeRTWrapper._invalidateLayeredFramebuffer();
2387
+ return;
2388
+ }
2389
+ if (nativeRTWrapper.isMulti) {
2390
+ if (nativeRTWrapper._framebuffer && this._isFrameGraphFramebuffer(nativeRTWrapper._framebuffer)) {
2391
+ nativeRTWrapper._framebuffer = null;
2392
+ }
2393
+ else if (nativeRTWrapper._framebuffer) {
2394
+ this._createMultiRenderTargetFramebuffer(nativeRTWrapper);
2395
+ }
2396
+ return;
2397
+ }
2398
+ const texture = nativeRTWrapper.texture;
2399
+ const nativeTexture = texture?._hardwareTexture?.underlyingResource;
2400
+ const depthTexture = nativeRTWrapper._depthStencilTexture;
2401
+ const nativeDepth = depthTexture?._hardwareTexture?.underlyingResource;
2402
+ if (nativeRTWrapper._framebuffers && nativeTexture) {
2403
+ if (nativeDepth) {
2404
+ this._attachDepthStencilTexture(nativeRTWrapper, depthTexture, nativeRTWrapper.width, nativeRTWrapper.height, nativeRTWrapper._depthStencilTextureWithStencil, nativeRTWrapper.samples);
2405
+ }
2406
+ else {
2407
+ nativeRTWrapper._framebuffers = this._createLayerFramebuffers(nativeTexture, nativeRTWrapper.width, nativeRTWrapper.height, nativeRTWrapper.isCube ? 6 : nativeRTWrapper.layers, nativeRTWrapper._generateStencilBuffer, nativeRTWrapper._generateDepthBuffer, nativeRTWrapper.samples, nativeRTWrapper.texture?.generateMipMaps);
2408
+ }
2409
+ return;
2410
+ }
2411
+ if (nativeDepth) {
2412
+ this._attachDepthStencilTexture(nativeRTWrapper, depthTexture, nativeRTWrapper.width, nativeRTWrapper.height, nativeRTWrapper._depthStencilTextureWithStencil, nativeRTWrapper.samples);
2413
+ }
2414
+ else {
2415
+ nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(nativeTexture ?? null, nativeRTWrapper.width, nativeRTWrapper.height, nativeRTWrapper._generateStencilBuffer, nativeRTWrapper._generateDepthBuffer, nativeRTWrapper.samples);
2416
+ }
2417
+ }
2418
+ /**
2419
+ * Rejects an explicit depth attachment the render target cannot use, before any attachment state is mutated.
2420
+ * bgfx requires every framebuffer attachment to share one sample count.
2421
+ * @internal
2422
+ */
2423
+ _assertDepthAttachmentCompatible(rtWrapper, depthSamples) {
2424
+ if (rtWrapper.is3D) {
2425
+ this._assertVolumeDepthAttachmentSupported();
2426
+ }
2427
+ if ((depthSamples || 1) !== (rtWrapper.samples || 1)) {
2428
+ throw new Error(`NativeEngine: explicit depth attachments must use the render target's sample count (depth: ${depthSamples || 1}, color: ${rtWrapper.samples || 1}).`);
2429
+ }
2430
+ }
2431
+ /**
2432
+ * Whether the depth attachment carries stencil: an explicit depth texture decides from its own format (its handle
2433
+ * may be unallocated after a sample-count change), otherwise the automatic depth buffer request does.
2434
+ * @internal
2435
+ */
2436
+ _getDepthAttachmentStencil(rtWrapper) {
2437
+ const depthTexture = rtWrapper._depthStencilTexture;
2438
+ return depthTexture ? HasStencilAspect(depthTexture.format) : rtWrapper._generateStencilBuffer;
2439
+ }
2440
+ /** @internal */
2441
+ _assertVolumeDepthAttachmentSupported(depthSamples, colorSamples) {
2442
+ if (!this._engine.createMultiFrameBuffer) {
2443
+ throw new Error("NativeEngine: explicit depth attachments on volume render targets require createMultiFrameBuffer support.");
2444
+ }
2445
+ if (depthSamples !== undefined && colorSamples !== undefined && depthSamples !== colorSamples) {
2446
+ throw new Error("NativeEngine: explicit depth and color attachments on volume render targets must use the same sample count.");
2447
+ }
2448
+ }
2449
+ /**
2450
+ * Updates a depth texture Comparison Mode and Function.
2451
+ * @param texture The texture to set the comparison function for
2452
+ * @param comparisonFunction The comparison function to set, 0 if no comparison required
2453
+ */
2454
+ updateTextureComparisonFunction(texture, comparisonFunction) {
2455
+ texture._comparisonFunction = comparisonFunction;
2456
+ // Drive bgfx BGFX_SAMPLER_COMPARE_* so PCF/PCSS shaders (sampler2DShadow / sampler2DArrayShadow)
2457
+ // get hardware depth compares. Engine-agnostic callers (LightingVolume) also toggle this around
2458
+ // a copy and expect the recorded value to stay in sync even when the hardware texture is missing.
2459
+ const nativeTexture = texture._hardwareTexture?.underlyingResource;
2460
+ if (nativeTexture && this._engine.setTextureComparisonFunction) {
2461
+ this._engine.setTextureComparisonFunction(nativeTexture, comparisonFunction);
2462
+ }
2463
+ }
2464
+ /**
2465
+ * Generates mipmaps for the given texture.
2466
+ * @param _texture The texture to generate the mipmaps for.
2467
+ */
2468
+ generateMipmaps(_texture) {
2469
+ // The WebGL implementation binds the texture and calls gl.generateMipmap; there is no equivalent
2470
+ // standalone command on Babylon Native, and none is needed: a render target whose mip chain must be
2471
+ // rebuilt is attached to its framebuffer with BGFX_RESOLVE_AUTO_GEN_MIPS (see CreateFrameBufferImpl),
2472
+ // so bgfx regenerates the chain when the view resolves. Without this override the inherited WebGL path
2473
+ // dereferenced a non-existent GL context (frame graph tasks call generateMipMaps explicitly).
2474
+ }
2475
+ /**
2476
+ * Creates one framebuffer per layer (or cube face) of a native texture.
2477
+ * @internal
2478
+ */
2479
+ _createLayerFramebuffers(nativeTexture, width, height, layerCount, generateStencilBuffer, generateDepthBuffer, samples, autoGenerateMips = true) {
2480
+ const framebuffers = [];
2481
+ for (let layer = 0; layer < layerCount; layer++) {
2482
+ framebuffers.push(this._engine.createFrameBuffer(nativeTexture, width, height, generateStencilBuffer, generateDepthBuffer, samples, layer, 0, autoGenerateMips));
2483
+ }
2484
+ return framebuffers;
2485
+ }
2486
+ /**
2487
+ * Per-layer framebuffers carry their own depth/stencil attachment, either requested at creation or added by
2488
+ * createDepthStencilTexture (see _createDepthStencilTexture), so recreate them with the same attachments.
2489
+ * @internal
2490
+ */
2491
+ _getLayerFramebufferDepthStencil(rtWrapper) {
2492
+ const hasDepthStencilTexture = !!rtWrapper._depthStencilTexture;
2493
+ return {
2494
+ generateDepth: rtWrapper._generateDepthBuffer || hasDepthStencilTexture,
2495
+ generateStencil: rtWrapper._generateStencilBuffer || (hasDepthStencilTexture && rtWrapper._depthStencilTextureWithStencil),
2496
+ };
2497
+ }
1779
2498
  /**
1780
2499
  * @internal
1781
2500
  */
1782
2501
  _releaseFramebufferObjects(framebuffer) {
1783
2502
  if (framebuffer) {
2503
+ // Frame graph framebuffers are shared across several render-target wrappers (see
2504
+ // _buildFrameGraphFramebuffer). Each wrapper releases its reference on dispose/reassignment, so
2505
+ // reference-count the shared framebuffer and only delete the underlying bgfx handle once.
2506
+ const shared = this._frameGraphFramebufferRefCount.get(framebuffer);
2507
+ if (shared !== undefined) {
2508
+ if (shared.count > 1) {
2509
+ shared.count--;
2510
+ return;
2511
+ }
2512
+ this._frameGraphFramebufferRefCount.delete(framebuffer);
2513
+ if (shared.hardwareTexture._frameGraphFramebuffer === framebuffer) {
2514
+ shared.hardwareTexture._frameGraphFramebuffer = null;
2515
+ }
2516
+ }
1784
2517
  this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEFRAMEBUFFER);
1785
2518
  this._commandBufferEncoder.encodeCommandArgAsNativeData(framebuffer);
1786
2519
  this._commandBufferEncoder.finishEncodingCommand();
1787
2520
  }
1788
2521
  }
2522
+ /**
2523
+ * Returns true when the given framebuffer was built by the frame graph path (_buildFrameGraphFramebuffer),
2524
+ * i.e. it is reference-counted, shared between wrappers and rebuilt lazily on bind.
2525
+ * @param framebuffer The framebuffer to test
2526
+ * @returns true if the framebuffer is a frame graph framebuffer
2527
+ * @internal
2528
+ */
2529
+ _isFrameGraphFramebuffer(framebuffer) {
2530
+ return this._frameGraphFramebufferRefCount.has(framebuffer);
2531
+ }
2532
+ // Reference counts + metadata for framebuffers shared across frame graph render-target wrappers
2533
+ // are stored in _frameGraphFramebufferRefCount (declared with the other engine fields).
2534
+ /**
2535
+ * Lazily builds (or reuses a shared) bgfx framebuffer for a frame graph render-target wrapper whose
2536
+ * textures were attached after creation (dontCreateTextures). All wrappers that reference the same
2537
+ * underlying color texture share a single framebuffer, cached on that texture's hardware wrapper, so that
2538
+ * successive passes (clear, object render, post) accumulate into the same target instead of each fresh
2539
+ * framebuffer/view clearing it.
2540
+ * @internal
2541
+ */
2542
+ _buildFrameGraphFramebuffer(nativeRTWrapper) {
2543
+ const textures = nativeRTWrapper.textures;
2544
+ if (!textures || textures.length === 0) {
2545
+ // Depth-only frame graph render target: 0 color attachments plus a shared depth-stencil texture.
2546
+ // The FrameGraph schedules a standalone depth-clear pass against the geometry buffer's depth texture,
2547
+ // separately from the geometry MRT render. Build a depth-only framebuffer that BORROWS the shared
2548
+ // depth texture (its bgfx handle is already valid, created up-front by _createInternalTexture) so the
2549
+ // clear hits the exact buffer the geometry MRT later depth-tests against. Without this the clear is
2550
+ // misdirected to the back buffer and the geometry prepass depth is never cleared, leaving the geometry
2551
+ // buffer empty (breaks SSR / motion blur / curvature / SSAO).
2552
+ const depthOnlyTexture = nativeRTWrapper._depthStencilTexture;
2553
+ const depthOnlyResource = depthOnlyTexture?._hardwareTexture?.underlyingResource;
2554
+ if (depthOnlyResource && !nativeRTWrapper._framebufferDepthStencil) {
2555
+ nativeRTWrapper._framebufferDepthStencil = this._createNativeMultiFrameBuffer([], nativeRTWrapper.width, nativeRTWrapper.height, nativeRTWrapper._generateStencilBuffer, true, depthOnlyTexture.samples || nativeRTWrapper.samples || 1, undefined, depthOnlyResource);
2556
+ }
2557
+ return;
2558
+ }
2559
+ const textureCount = textures.length;
2560
+ for (let i = 0; i < textureCount; i++) {
2561
+ if (!textures[i]?._hardwareTexture?.underlyingResource) {
2562
+ // A color texture is not yet backed by a hardware texture; leave the wrapper unbuilt (the base
2563
+ // path will bind the back buffer). This wrapper will be retried on its next bind.
2564
+ return;
2565
+ }
2566
+ }
2567
+ const hardwareTexture = textures[0]._hardwareTexture;
2568
+ const depthStencilTexture = nativeRTWrapper._depthStencilTexture;
2569
+ const generateDepthBuffer = !!depthStencilTexture || nativeRTWrapper._generateDepthBuffer;
2570
+ const generateStencilBuffer = this._getDepthAttachmentStencil(nativeRTWrapper);
2571
+ const samples = textures[0].samples || nativeRTWrapper.samples || 1;
2572
+ // Like a GL framebuffer, an explicit depth-stencil texture is always attached as-is, so every pass that
2573
+ // references it (clears, geometry, overlays, later samplers) sees the same real depth buffer.
2574
+ const sharedDepthResource = depthStencilTexture?._hardwareTexture?.underlyingResource;
2575
+ const cached = hardwareTexture._frameGraphFramebuffer;
2576
+ const cachedMeta = cached ? this._frameGraphFramebufferRefCount.get(cached) : undefined;
2577
+ // Reuse the cached framebuffer when it is compatible: the same ordered color attachments (a swap of any
2578
+ // attachment, not only the primary one, needs a new framebuffer), the same sample count, depth/stencil
2579
+ // buffers when this pass needs them (a pass that does not need them can safely reuse a framebuffer that
2580
+ // has them), and the same shared depth texture (so a shared-depth pass never reuses an auto-depth
2581
+ // framebuffer, and vice versa). History-texture swaps invalidate these wrappers every frame, so this check
2582
+ // runs in the render loop: compare in place and only allocate the color list for a new framebuffer.
2583
+ let sameColors = !!cachedMeta && cachedMeta.colorResources.length === textureCount;
2584
+ for (let i = 0; sameColors && i < textureCount; i++) {
2585
+ sameColors = cachedMeta.colorResources[i] === textures[i]._hardwareTexture.underlyingResource;
2586
+ }
2587
+ if (cached &&
2588
+ cachedMeta &&
2589
+ sameColors &&
2590
+ cachedMeta.samples === samples &&
2591
+ (cachedMeta.hasDepth || !generateDepthBuffer) &&
2592
+ (cachedMeta.hasStencil || !generateStencilBuffer) &&
2593
+ cachedMeta.sharedDepthResource === sharedDepthResource) {
2594
+ cachedMeta.count++;
2595
+ nativeRTWrapper._framebuffer = cached;
2596
+ return;
2597
+ }
2598
+ const colorTextures = new Array(textureCount);
2599
+ for (let i = 0; i < textureCount; i++) {
2600
+ colorTextures[i] = textures[i]._hardwareTexture.underlyingResource;
2601
+ }
2602
+ const framebuffer = sharedDepthResource
2603
+ ? this._createNativeMultiFrameBuffer(colorTextures, nativeRTWrapper.width, nativeRTWrapper.height, generateStencilBuffer, true, samples, undefined, sharedDepthResource)
2604
+ : colorTextures.length === 1
2605
+ ? this._engine.createFrameBuffer(colorTextures[0], nativeRTWrapper.width, nativeRTWrapper.height, generateStencilBuffer, generateDepthBuffer, samples)
2606
+ : this._createNativeMultiFrameBuffer(colorTextures, nativeRTWrapper.width, nativeRTWrapper.height, generateStencilBuffer, generateDepthBuffer, samples);
2607
+ this._frameGraphFramebufferRefCount.set(framebuffer, {
2608
+ count: 1,
2609
+ hardwareTexture,
2610
+ colorResources: colorTextures,
2611
+ hasDepth: generateDepthBuffer,
2612
+ hasStencil: generateStencilBuffer,
2613
+ samples,
2614
+ sharedDepthResource,
2615
+ });
2616
+ hardwareTexture._frameGraphFramebuffer = framebuffer;
2617
+ nativeRTWrapper._framebuffer = framebuffer;
2618
+ }
1789
2619
  /**
1790
2620
  * @internal Engine abstraction for loading and creating an image bitmap from a given source string.
1791
2621
  * @param imageSource source to load the image from.
@@ -1853,9 +2683,19 @@ export class ThinNativeEngine extends ThinEngine {
1853
2683
  this._renderTargetWrapperCache.push(rtWrapper);
1854
2684
  return rtWrapper;
1855
2685
  }
2686
+ /**
2687
+ * Older bgfx D3D11/D3D12/Vulkan backends share one texture descriptor between the MSAA render target and its
2688
+ * resolve target, so requesting mips together with samples > 1 fails texture creation (BabylonNative#1714).
2689
+ * Only keep the mip chain of a multisampled render target when the runtime reports that it supports it.
2690
+ * @internal
2691
+ */
2692
+ _getRenderTargetHasMips(generateMipMaps, samples) {
2693
+ return generateMipMaps && (samples <= 1 || !!this._engine.supportsMultisampledMipmaps);
2694
+ }
1856
2695
  /** @internal */
1857
2696
  _createInternalTexture(size, options, _delayGPUTextureCreation = true, source = 0 /* InternalTextureSource.Unknown */) {
1858
2697
  let generateMipMaps;
2698
+ let createMipMaps = false;
1859
2699
  let type = 0;
1860
2700
  let samplingMode = 3;
1861
2701
  let format = 5;
@@ -1864,6 +2704,7 @@ export class ThinNativeEngine extends ThinEngine {
1864
2704
  let label;
1865
2705
  if (options !== undefined && typeof options === "object") {
1866
2706
  generateMipMaps = !!options.generateMipMaps;
2707
+ createMipMaps = !!options.createMipMaps;
1867
2708
  type = options.type === undefined ? 0 : options.type;
1868
2709
  samplingMode = options.samplingMode === undefined ? 3 : options.samplingMode;
1869
2710
  format = options.format === undefined ? 5 : options.format;
@@ -1887,37 +2728,49 @@ export class ThinNativeEngine extends ThinEngine {
1887
2728
  type = 0;
1888
2729
  Logger.Warn("Float textures are not supported. Type forced to TEXTURETYPE_UNSIGNED_BYTE");
1889
2730
  }
2731
+ // A cube render-target attachment routed through _createInternalTexture (e.g. a frame-graph MRT cube
2732
+ // target: the texture manager creates each attachment via _createInternalTexture and sets isCube for a
2733
+ // TEXTURE_CUBE_MAP target) must be created as a real bgfx cube texture. This path otherwise only
2734
+ // produces 2D / 2D-array textures (isCube was silently dropped), so a samplerCube read it back as a
2735
+ // plain 2D texture and the cube attachment rendered/sampled wrong. Delegate to the cube path so its
2736
+ // per-face layers (layer*6+face) are addressable.
2737
+ if (typeof options === "object" && options.isCube) {
2738
+ const cubeSize = size.width ?? size;
2739
+ return this._createInternalCubeTexture(cubeSize, options, source);
2740
+ }
1890
2741
  const texture = new InternalTexture(this, source);
1891
2742
  const width = size.width ?? size;
1892
2743
  const height = size.height ?? size;
1893
2744
  const layers = size.layers || 0;
1894
- if (layers !== 0) {
1895
- throw new Error("Texture layers are not supported in Babylon Native");
1896
- }
2745
+ // Like WebGL, a depth (without layers) requests a volume texture (e.g. a TEXTURE_3D MRT attachment).
2746
+ const depth = layers ? 0 : size.depth || 0;
1897
2747
  const nativeTexture = texture._hardwareTexture.underlyingResource;
1898
2748
  const nativeTextureFormat = getNativeTextureFormat(format, type);
1899
- // TODO(bgfx-msaa-mips): stopgap workaround for a bgfx bug -- D3D11/D3D12/Vulkan backends share one
1900
- // texture descriptor between the MSAA render target and the non-MSAA resolve target, so requesting
1901
- // both mips > 1 and samples > 1 makes the API (D3D11 E_INVALIDARG, Vulkan VUID-02257, ...) reject the
1902
- // MSAA texture creation. Force hasMips = false here to keep the combo from reaching bgfx. The fix
1903
- // belongs in bgfx (separate descs per target, like OpenGL/WebGL do with a non-mipped renderbuffer);
1904
- // this guard should be removed once a fixed bgfx ships in a stable BabylonNative npm release. Tracked
1905
- // in BabylonNative#1714. Cost: MSAA RTs on Native lose post-resolve auto-mipgen and diverge from
1906
- // WebGL/WebGPU semantics -- texture.generateMipMaps stays true on the InternalTexture but the
1907
- // underlying bgfx resource has 1 mip.
1908
- const hasMips = samples > 1 ? false : generateMipMaps;
2749
+ // createMipMaps allocates mip storage (e.g. FrameGraph textures) without requesting automatic generation.
2750
+ const hasMips = this._getRenderTargetHasMips(generateMipMaps || createMipMaps, samples);
1909
2751
  // REVIEW: We are always setting the renderTarget flag as we don't know whether the texture will be used as a render target.
1910
- this._engine.initializeTexture(nativeTexture, width, height, hasMips, nativeTextureFormat, true, useSRGBBuffer, samples);
2752
+ // A layers > 0 request creates a 2D texture array (e.g. a cascaded-shadow-map render target); the
2753
+ // matching per-layer framebuffers are built by createRenderTargetTexture and bound via bindFramebuffer(layer).
2754
+ this._engine.initializeTexture(nativeTexture, width, height, hasMips, nativeTextureFormat, true, useSRGBBuffer, samples, false, layers || depth, depth > 0);
1911
2755
  this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));
1912
2756
  texture._useSRGBBuffer = useSRGBBuffer;
1913
2757
  texture.baseWidth = width;
1914
2758
  texture.baseHeight = height;
1915
2759
  texture.width = width;
1916
2760
  texture.height = height;
1917
- texture.depth = layers;
2761
+ texture.depth = layers || depth;
2762
+ if (layers > 0) {
2763
+ texture.is2DArray = true;
2764
+ texture.baseDepth = layers;
2765
+ }
2766
+ else if (depth > 0) {
2767
+ texture.is3D = true;
2768
+ texture.baseDepth = depth;
2769
+ }
1918
2770
  texture.isReady = true;
1919
2771
  texture.samples = samples;
1920
2772
  texture.generateMipMaps = generateMipMaps;
2773
+ this._setHasMipStorage(texture, generateMipMaps || createMipMaps);
1921
2774
  texture.samplingMode = samplingMode;
1922
2775
  texture.type = type;
1923
2776
  texture.format = format;
@@ -1925,6 +2778,52 @@ export class ThinNativeEngine extends ThinEngine {
1925
2778
  this._internalTexturesCache.push(texture);
1926
2779
  return texture;
1927
2780
  }
2781
+ // Creates a standalone, sampleable cube InternalTexture usable as a mixed-type MRT color attachment (a
2782
+ // single face is targeted per attachment via the framebuffer's per-attachment layer). Mirrors
2783
+ // _createInternalTexture's float/half-float linear-filter fallbacks + cache registration, but initializes
2784
+ // the bgfx texture as a cube render target (there is no cube path through _createInternalTexture, which
2785
+ // only produces 2D / 2D-array textures).
2786
+ _createInternalCubeTexture(size, options, source) {
2787
+ let type = options.type ?? 0;
2788
+ let samplingMode = options.samplingMode ?? 3;
2789
+ const format = options.format ?? 5;
2790
+ const generateMipMaps = !!options.generateMipMaps;
2791
+ const samples = options.samples ?? 1;
2792
+ const useSRGBBuffer = this._getUseSRGBBuffer(!!options.useSRGBBuffer, !generateMipMaps);
2793
+ if (type === 1 && !this._caps.textureFloatLinearFiltering) {
2794
+ samplingMode = 1;
2795
+ }
2796
+ else if (type === 2 && !this._caps.textureHalfFloatLinearFiltering) {
2797
+ samplingMode = 1;
2798
+ }
2799
+ if (type === 1 && !this._caps.textureFloat) {
2800
+ type = 0;
2801
+ Logger.Warn("Float textures are not supported. Type forced to TEXTURETYPE_UNSIGNED_BYTE");
2802
+ }
2803
+ const texture = new InternalTexture(this, source);
2804
+ texture.isCube = true;
2805
+ texture.baseWidth = size;
2806
+ texture.baseHeight = size;
2807
+ texture.width = size;
2808
+ texture.height = size;
2809
+ texture.isReady = true;
2810
+ texture.samples = samples;
2811
+ texture.generateMipMaps = generateMipMaps;
2812
+ this._setHasMipStorage(texture, generateMipMaps || !!options.createMipMaps);
2813
+ texture.samplingMode = samplingMode;
2814
+ texture.type = type;
2815
+ texture.format = format;
2816
+ texture.label = options.label;
2817
+ texture._useSRGBBuffer = useSRGBBuffer;
2818
+ const nativeTexture = texture._hardwareTexture.underlyingResource;
2819
+ const nativeTextureFormat = getNativeTextureFormat(format, type);
2820
+ // createMipMaps allocates mip storage (e.g. FrameGraph textures) without requesting automatic generation.
2821
+ const hasMips = this._getRenderTargetHasMips(generateMipMaps || !!options.createMipMaps, samples);
2822
+ this._engine.initializeTexture(nativeTexture, size, size, hasMips, nativeTextureFormat, /*renderTarget*/ true, useSRGBBuffer, samples, /*isCube*/ true);
2823
+ this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));
2824
+ this._internalTexturesCache.push(texture);
2825
+ return texture;
2826
+ }
1928
2827
  createRenderTargetTexture(size, options) {
1929
2828
  const rtWrapper = this._createHardwareRenderTargetWrapper(false, false, size);
1930
2829
  let generateDepthBuffer = true;
@@ -1939,9 +2838,27 @@ export class ThinNativeEngine extends ThinEngine {
1939
2838
  colorAttachment = options.colorAttachment;
1940
2839
  samples = options.samples ?? 1;
1941
2840
  }
2841
+ const width = typeof size === "number" ? size : size.width;
2842
+ const height = typeof size === "number" ? size : size.height;
2843
+ const layers = typeof size === "number" ? 0 : size.layers || 0;
2844
+ const depth = typeof size === "number" ? 0 : size.depth || 0;
2845
+ // 3D render target (IBL voxel grid + its procedural mip chain): create a real volume texture and
2846
+ // render to each Z-slice / mip through its own lazily-built framebuffer (see _get3DLayerFramebuffer).
2847
+ if (depth > 0 && !noColorAttachment && !colorAttachment) {
2848
+ return this._createRenderTargetTexture3D(rtWrapper, options, width, height, depth, generateDepthBuffer, generateStencilBuffer);
2849
+ }
1942
2850
  const texture = colorAttachment || (noColorAttachment ? null : this._createInternalTexture(size, options, true, 5 /* InternalTextureSource.RenderTarget */));
1943
- const width = size.width ?? size;
1944
- const height = size.height ?? size;
2851
+ if (layers > 0 && texture) {
2852
+ // 2D texture array render target (e.g. cascaded shadow maps): the native engine renders each
2853
+ // array layer through its own framebuffer bound to that layer (mirroring the cube per-face path);
2854
+ // bindFramebuffer(layerIndex) then selects the right one.
2855
+ rtWrapper._framebuffers = this._createLayerFramebuffers(texture._hardwareTexture.underlyingResource, width, height, layers, generateStencilBuffer, generateDepthBuffer, samples, texture.generateMipMaps);
2856
+ rtWrapper._generateDepthBuffer = generateDepthBuffer;
2857
+ rtWrapper._generateStencilBuffer = generateStencilBuffer;
2858
+ rtWrapper._samples = samples;
2859
+ rtWrapper.setTextures(texture);
2860
+ return rtWrapper;
2861
+ }
1945
2862
  const framebuffer = this._engine.createFrameBuffer(texture ? texture._hardwareTexture.underlyingResource : null, width, height, generateStencilBuffer, generateDepthBuffer, samples);
1946
2863
  rtWrapper._framebuffer = framebuffer;
1947
2864
  rtWrapper._generateDepthBuffer = generateDepthBuffer;
@@ -1950,11 +2867,22 @@ export class ThinNativeEngine extends ThinEngine {
1950
2867
  rtWrapper.setTextures(texture);
1951
2868
  return rtWrapper;
1952
2869
  }
2870
+ // Builds a 3D (volume) render-target texture. bgfx renders to individual Z-slices/mips through per-slice
2871
+ // framebuffers created on demand in _get3DLayerFramebuffer; the volume is sampled as a sampler3D.
2872
+ _createRenderTargetTexture3D(rtWrapper, options, width, height, depth, generateDepthBuffer, generateStencilBuffer) {
2873
+ const texture = this._createInternalTexture({ width, height, depth }, options, true, 5 /* InternalTextureSource.RenderTarget */);
2874
+ rtWrapper._generateDepthBuffer = generateDepthBuffer;
2875
+ rtWrapper._generateStencilBuffer = generateStencilBuffer;
2876
+ rtWrapper._samples = texture.samples;
2877
+ rtWrapper.setTextures(texture);
2878
+ return rtWrapper;
2879
+ }
1953
2880
  createRenderTargetCubeTexture(size, options) {
1954
2881
  const rtWrapper = this._createHardwareRenderTargetWrapper(false, true, size);
1955
2882
  let generateDepthBuffer = true;
1956
2883
  let generateStencilBuffer = false;
1957
2884
  let generateMipMaps = false;
2885
+ let createMipMaps;
1958
2886
  let type = 0;
1959
2887
  let samplingMode = 3;
1960
2888
  let format = 5;
@@ -1964,12 +2892,17 @@ export class ThinNativeEngine extends ThinEngine {
1964
2892
  generateDepthBuffer = options.generateDepthBuffer ?? true;
1965
2893
  generateStencilBuffer = !!options.generateStencilBuffer;
1966
2894
  generateMipMaps = !!options.generateMipMaps;
2895
+ createMipMaps = options.createMipMaps;
1967
2896
  type = options.type ?? 0;
1968
2897
  samplingMode = options.samplingMode ?? 3;
1969
2898
  format = options.format ?? 5;
1970
2899
  samples = options.samples ?? 1;
1971
2900
  label = options.label;
1972
2901
  }
2902
+ // Storage for a full mip chain is requested via createMipMaps (falling back to generateMipMaps when
2903
+ // unspecified). HDR prefiltering passes createMipMaps:true + generateMipMaps:false because it renders
2904
+ // each roughness mip explicitly and must NOT have them auto-regenerated on unbind.
2905
+ const allocateMips = createMipMaps ?? generateMipMaps;
1973
2906
  // Match _createInternalTexture: float/half-float RTTs that the platform can't linearly filter fall
1974
2907
  // back to NEAREST so the cube RTT never carries an unsupported sampling mode.
1975
2908
  if (type === 1 && !this._caps.textureFloatLinearFiltering) {
@@ -1991,6 +2924,7 @@ export class ThinNativeEngine extends ThinEngine {
1991
2924
  texture.isReady = true;
1992
2925
  texture.samples = samples;
1993
2926
  texture.generateMipMaps = generateMipMaps;
2927
+ this._setHasMipStorage(texture, allocateMips);
1994
2928
  texture.samplingMode = samplingMode;
1995
2929
  texture.type = type;
1996
2930
  texture.format = format;
@@ -1998,15 +2932,19 @@ export class ThinNativeEngine extends ThinEngine {
1998
2932
  const nativeTexture = texture._hardwareTexture.underlyingResource;
1999
2933
  const nativeTextureFormat = getNativeTextureFormat(format, type);
2000
2934
  // See the createRenderTargetTexture MSAA/mips note: avoid the mips + samples combo on bgfx.
2001
- const hasMips = samples > 1 ? false : generateMipMaps;
2935
+ const hasMips = this._getRenderTargetHasMips(allocateMips, samples);
2002
2936
  this._engine.initializeTexture(nativeTexture, size, size, hasMips, nativeTextureFormat, /*renderTarget*/ true, /*srgb*/ false, samples, /*isCube*/ true);
2003
2937
  this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));
2004
2938
  // The native engine cannot render to all six faces through one framebuffer, so create one
2005
2939
  // framebuffer per face (the C++ side binds the matching cube layer); bindFramebuffer(faceIndex)
2006
2940
  // then selects the right one.
2941
+ // generateMipMaps is forwarded as autoGenerateMips: a cube RTT that authors its own mip levels
2942
+ // (HDR radiance prefiltering passes createMipMaps:true + generateMipMaps:false and renders one
2943
+ // convolution per face+mip) must not have the chain regenerated from mip 0 when a face resolves,
2944
+ // which would wipe every explicitly rendered roughness level.
2007
2945
  const framebuffers = [];
2008
2946
  for (let face = 0; face < 6; face++) {
2009
- framebuffers.push(this._engine.createFrameBuffer(nativeTexture, size, size, generateStencilBuffer, generateDepthBuffer, samples, face));
2947
+ framebuffers.push(this._engine.createFrameBuffer(nativeTexture, size, size, generateStencilBuffer, generateDepthBuffer, samples, face, 0, generateMipMaps));
2010
2948
  }
2011
2949
  rtWrapper._framebuffers = framebuffers;
2012
2950
  rtWrapper._generateDepthBuffer = generateDepthBuffer;
@@ -2018,91 +2956,130 @@ export class ThinNativeEngine extends ThinEngine {
2018
2956
  this._internalTexturesCache.push(texture);
2019
2957
  return rtWrapper;
2020
2958
  }
2959
+ // The Native engine renders a multi render target through a single bgfx framebuffer with several color
2960
+ // attachments (see NativeEngine.cpp CreateMultiFrameBuffer / CreateFrameBufferImpl). Plain 2D color
2961
+ // attachments (the WebGL prepass / geometry-buffer path) build their framebuffer eagerly here; mixed-type
2962
+ // attachments (specific cube faces / 2D-array layers) and 3D voxelization MRTs build a layered
2963
+ // multi-attachment framebuffer lazily on first bind. Sampleable depth textures (generateDepthTexture)
2964
+ // are not supported. Attachments swapped after creation (e.g. the OIT depth-peeling renderer) rebuild the
2965
+ // framebuffer via _createMultiRenderTargetFramebuffer (called from NativeRenderTargetWrapper.setTexture).
2021
2966
  createMultipleRenderTarget(size, options, _initializeBuffers = true) {
2022
- const rtWrapper = this._createHardwareRenderTargetWrapper(true, false, size);
2023
2967
  let generateMipMaps = false;
2968
+ let createMipMaps = false;
2024
2969
  let generateDepthBuffer = true;
2025
2970
  let generateStencilBuffer = false;
2026
2971
  let generateDepthTexture = false;
2027
2972
  let textureCount = 1;
2028
2973
  let samples = 1;
2974
+ const defaultType = 0;
2975
+ const defaultSamplingMode = 3;
2976
+ const defaultFormat = 5;
2029
2977
  let types = [];
2030
2978
  let samplingModes = [];
2979
+ let useSRGBBuffers = [];
2031
2980
  let formats = [];
2032
2981
  let targets = [];
2033
- let faceIndex = [];
2034
- let layerIndex = [];
2035
2982
  let labels = [];
2036
2983
  let dontCreateTextures = false;
2984
+ const rtWrapper = this._createHardwareRenderTargetWrapper(true, false, size);
2037
2985
  if (options !== undefined) {
2038
2986
  generateMipMaps = options.generateMipMaps ?? false;
2987
+ createMipMaps = options.createMipMaps ?? generateMipMaps;
2039
2988
  generateDepthBuffer = options.generateDepthBuffer ?? true;
2040
2989
  generateStencilBuffer = options.generateStencilBuffer ?? false;
2041
2990
  generateDepthTexture = options.generateDepthTexture ?? false;
2042
2991
  textureCount = options.textureCount ?? 1;
2043
- samples = options.samples ?? 1;
2992
+ samples = options.samples ?? samples;
2044
2993
  types = options.types || types;
2045
2994
  samplingModes = options.samplingModes || samplingModes;
2995
+ useSRGBBuffers = options.useSRGBBuffers || useSRGBBuffers;
2046
2996
  formats = options.formats || formats;
2047
2997
  targets = options.targetTypes || targets;
2048
- faceIndex = options.faceIndex || faceIndex;
2049
- layerIndex = options.layerIndex || layerIndex;
2050
2998
  labels = options.labels || labels;
2051
2999
  dontCreateTextures = options.dontCreateTextures ?? false;
2052
3000
  }
2053
- rtWrapper.label = options?.label ?? "MultiRenderTargetWrapper";
2054
3001
  const width = size.width ?? size;
2055
3002
  const height = size.height ?? size;
3003
+ // MRT whose color attachments are distinct layers of one shared 3D texture (IBL voxelization: N draw
3004
+ // buffers → N Z-slices of the voxel grid). The shared texture is assigned later via setInternalTexture,
3005
+ // so the layered multi-attachment framebuffer is (re)built lazily on first bind (_bindLayeredMultiFramebuffer).
3006
+ const layerIndex = options?.layerIndex;
3007
+ const faceIndex = options?.faceIndex;
3008
+ const layerCounts = options?.layerCounts;
3009
+ const is3DLayeredMRT = targets.some((t) => t === 32879);
3010
+ // A mixed-type MRT renders each color attachment into a specific layer of a 2D-array texture or a
3011
+ // specific face of a cube texture (alongside plain 2D targets). Detect it so each color texture is
3012
+ // created with the correct dimensionality and the multi-attachment framebuffer is built lazily with
3013
+ // per-attachment layer/face (setInternalTexture may swap a shared texture in after creation, e.g. the
3014
+ // MRT that renders two different layers of one 2D-array via a -1 target + setInternalTexture).
3015
+ const isLayeredMRT = is3DLayeredMRT || targets.some((t) => t === 35866 || t === 34067);
2056
3016
  const textures = [];
2057
3017
  const attachments = [];
3018
+ rtWrapper.label = options?.label ?? "MultiRenderTargetWrapper";
3019
+ // Without a sampleable depth attachment on Native, a generateDepthTexture request still needs depth testing.
3020
+ rtWrapper._generateDepthBuffer = generateDepthBuffer || generateDepthTexture;
3021
+ rtWrapper._generateStencilBuffer = generateStencilBuffer;
3022
+ rtWrapper._attachments = attachments;
2058
3023
  for (let i = 0; i < textureCount; i++) {
2059
- const samplingMode = samplingModes[i] || 3;
2060
- let type = types[i] || 0;
2061
- const format = formats[i] || 5;
2062
- const target = targets[i] || 3553;
2063
- attachments.push(i);
2064
- // target === -1 marks an attachment with no engine-created texture; dontCreateTextures defers them.
3024
+ const samplingMode = samplingModes[i] || defaultSamplingMode;
3025
+ const type = types[i] || defaultType;
3026
+ const format = formats[i] || defaultFormat;
3027
+ const useSRGBBuffer = (useSRGBBuffers[i] || false) && this._caps.supportSRGBBuffers;
3028
+ const target = targets[i];
3029
+ // Attachment index i+1 mirrors the WebGL COLOR_ATTACHMENTi convention consumers rely on.
3030
+ attachments.push(i + 1);
2065
3031
  if (target === -1 || dontCreateTextures) {
2066
3032
  continue;
2067
3033
  }
2068
- if (type === 1 && !this._caps.textureFloat) {
2069
- type = 0;
2070
- Logger.Warn("Float textures are not supported. Multi render target attachment forced to TEXTURETYPE_UNSIGNED_BYTE type");
3034
+ // _createInternalTexture initializes the bgfx texture as a sampleable render target and applies
3035
+ // the float/half-float linear-filter fallbacks + cache registration, matching createRenderTargetTexture.
3036
+ // The color attachments must carry the same MSAA sample count as the framebuffer's depth attachment
3037
+ // (created with `samples` below): bgfx rejects a framebuffer that mixes single-sample color targets
3038
+ // with a multisample depth target, which surfaced as "Failed to create frame buffer" for MSAA MRTs
3039
+ // (e.g. the SSAO prepass). _createInternalTexture already drops mips when samples > 1.
3040
+ // A mixed-type MRT creates the attachment with the dimensionality requested by targetTypes: a cube
3041
+ // texture (one face targeted per attachment) or a 2D-array texture with layerCounts layers (one
3042
+ // layer targeted per attachment). The framebuffer selects the specific face/layer per attachment.
3043
+ const textureLabel = labels[i] ?? rtWrapper.label + "-Texture" + i;
3044
+ const textureOptions = { generateMipMaps, createMipMaps, type, format, samplingMode, useSRGBBuffer, samples, label: textureLabel };
3045
+ let texture;
3046
+ if (target === 34067) {
3047
+ texture = this._createInternalCubeTexture(width, textureOptions, 6 /* InternalTextureSource.MultiRenderTarget */);
2071
3048
  }
2072
- const texture = new InternalTexture(this, 6 /* InternalTextureSource.MultiRenderTarget */);
2073
- const nativeTexture = texture._hardwareTexture.underlyingResource;
2074
- if (target !== 3553) {
2075
- // Native multi render targets currently only attach 2D color textures; cube / 2D-array
2076
- // attachments are created as 2D so the attachment still exists and mrt.textures[i] is populated.
2077
- Logger.Warn("Multi render target attachment target " + target + " is not supported on Native; using a 2D texture.");
3049
+ else if (target === 35866) {
3050
+ const layerCount = Math.max(1, layerCounts?.[i] ?? 1);
3051
+ texture = this._createInternalTexture({ width, height, layers: layerCount }, textureOptions, true, 6 /* InternalTextureSource.MultiRenderTarget */);
2078
3052
  }
2079
- // bgfx auto-generates the mip chain on resolve; avoid the mips + MSAA combo (see createRenderTargetTexture).
2080
- const hasMips = samples > 1 ? false : generateMipMaps;
2081
- this._engine.initializeTexture(nativeTexture, width, height, hasMips, getNativeTextureFormat(format, type), /*renderTarget*/ true, /*srgb*/ false, samples);
2082
- this._setTextureSampling(nativeTexture, getNativeSamplingMode(samplingMode));
2083
- texture.baseWidth = width;
2084
- texture.baseHeight = height;
2085
- texture.width = width;
2086
- texture.height = height;
2087
- texture.isReady = true;
2088
- texture.samples = samples;
2089
- texture.generateMipMaps = generateMipMaps;
2090
- texture.samplingMode = samplingMode;
2091
- texture.type = type;
2092
- texture.format = format;
2093
- texture.label = labels[i] ?? rtWrapper.label + "-Texture" + i;
3053
+ else if (target === 32879) {
3054
+ const depth = Math.max(1, layerCounts?.[i] || size.depth || 1);
3055
+ texture = this._createInternalTexture({ width, height, depth }, textureOptions, true, 6 /* InternalTextureSource.MultiRenderTarget */);
3056
+ }
3057
+ else {
3058
+ texture = this._createInternalTexture({ width, height }, textureOptions, true, 6 /* InternalTextureSource.MultiRenderTarget */);
3059
+ }
3060
+ texture._cachedWrapU = 0;
3061
+ texture._cachedWrapV = 0;
2094
3062
  textures[i] = texture;
2095
- this._internalTexturesCache.push(texture);
2096
3063
  }
2097
- rtWrapper._generateDepthBuffer = generateDepthBuffer || generateDepthTexture;
2098
- rtWrapper._generateStencilBuffer = generateStencilBuffer;
3064
+ if (generateDepthTexture && !dontCreateTextures) {
3065
+ // The multi-framebuffer path allocates its own (non-sampleable) depth/stencil buffer; a separate
3066
+ // sampleable depth texture attachment is not wired up on Native.
3067
+ Logger.Warn("NativeEngine.createMultipleRenderTarget: generateDepthTexture is not supported; using a non-sampleable depth buffer.");
3068
+ }
3069
+ if (isLayeredMRT || layerIndex) {
3070
+ rtWrapper.setLayerAndFaceIndices(layerIndex ?? [], faceIndex ?? []);
3071
+ }
3072
+ // Layered MRTs (3D / 2D-array / cube attachments, possibly mixed with 2D) build their multi-attachment
3073
+ // framebuffer lazily on first bind. The flag selects that path in bindFramebuffer so per-attachment
3074
+ // layer/face and post-creation setInternalTexture are honored, even when the size carries no depth.
3075
+ if (isLayeredMRT) {
3076
+ rtWrapper._isMixedTypeMRT = true;
3077
+ }
2099
3078
  rtWrapper._samples = samples;
2100
- rtWrapper._attachments = attachments;
2101
3079
  rtWrapper.setTextures(textures);
2102
- rtWrapper.setLayerAndFaceIndices(layerIndex, faceIndex);
2103
- // The native engine creates one framebuffer with all the color attachments (bgfx writes to every
2104
- // attachment of the bound framebuffer, so there is no drawBuffers equivalent to issue per draw).
2105
- this._createMultiRenderTargetFramebuffer(rtWrapper);
3080
+ if (!dontCreateTextures && !isLayeredMRT) {
3081
+ this._createMultiRenderTargetFramebuffer(rtWrapper);
3082
+ }
2106
3083
  return rtWrapper;
2107
3084
  }
2108
3085
  /**
@@ -2137,16 +3114,34 @@ export class ThinNativeEngine extends ThinEngine {
2137
3114
  }
2138
3115
  const width = rtWrapper.width;
2139
3116
  const height = rtWrapper.height;
2140
- if (this._engine.createMultiFrameBuffer) {
2141
- rtWrapper._framebuffer = this._engine.createMultiFrameBuffer(colorHandles, width, height, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer, rtWrapper._samples);
3117
+ // Prefer an explicit shared depth texture when present (OIT depth peeling shareDepth, FG depth
3118
+ // attached after dontCreateTextures). Otherwise fall back to auto depth from _generateDepthBuffer.
3119
+ const explicitDepth = rtWrapper._depthStencilTexture?._hardwareTexture?.underlyingResource;
3120
+ const generateDepthBuffer = !!explicitDepth || rtWrapper._generateDepthBuffer;
3121
+ rtWrapper._framebuffer = this._createNativeMultiFrameBuffer(colorHandles, width, height, this._getDepthAttachmentStencil(rtWrapper), generateDepthBuffer, rtWrapper._samples, undefined, explicitDepth);
3122
+ this._applyDepthTextureSampling(rtWrapper);
3123
+ }
3124
+ _applyDepthTextureSampling(rtWrapper) {
3125
+ const depthTexture = rtWrapper._depthStencilTexture;
3126
+ const nativeDepth = depthTexture?._hardwareTexture?.underlyingResource;
3127
+ if (!depthTexture || !nativeDepth) {
3128
+ return;
2142
3129
  }
2143
- else {
2144
- // Older Babylon Native binaries (predating multi render target support) do not expose createMultiFrameBuffer.
2145
- // Fall back to a single-attachment framebuffer bound to the first color target so the scene keeps rendering.
2146
- // Warn once (limit 1): the framebuffer can be rebuilt many times during attachment setup/swaps.
2147
- Logger.Warn("createMultiFrameBuffer is not supported by this version of Babylon Native; multi render targets are unavailable. Falling back to a single-attachment framebuffer bound to the first color target.", 1);
2148
- rtWrapper._framebuffer = this._engine.createFrameBuffer(colorHandles[0], width, height, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer, rtWrapper._samples);
3130
+ this._setTextureSampling(nativeDepth, getNativeSamplingMode(depthTexture.samplingMode));
3131
+ this.updateTextureComparisonFunction(depthTexture, depthTexture._comparisonFunction);
3132
+ }
3133
+ /**
3134
+ * Creates a multi-attachment framebuffer, falling back to a single-attachment framebuffer bound to the first
3135
+ * color target (and its first requested layer) on older Babylon Native runtimes without createMultiFrameBuffer.
3136
+ * @internal
3137
+ */
3138
+ _createNativeMultiFrameBuffer(colorTextures, width, height, generateStencilBuffer, generateDepthBuffer, samples, layers, explicitDepthTexture) {
3139
+ if (this._engine.createMultiFrameBuffer) {
3140
+ return this._engine.createMultiFrameBuffer(colorTextures, width, height, generateStencilBuffer, generateDepthBuffer, samples, layers, explicitDepthTexture);
2149
3141
  }
3142
+ // Warn once (limit 1): the framebuffer can be rebuilt many times during attachment setup/swaps.
3143
+ Logger.Warn("createMultiFrameBuffer is not supported by this version of Babylon Native; multi render targets are unavailable. Falling back to a single-attachment framebuffer bound to the first color target.", 1);
3144
+ return this._engine.createFrameBuffer(colorTextures[0] ?? null, width, height, generateStencilBuffer, generateDepthBuffer, samples, layers?.[0]);
2150
3145
  }
2151
3146
  generateMipMapsForCubemap(_texture, _unbind = true) {
2152
3147
  // The WebGL path rebinds gl.TEXTURE_CUBE_MAP and calls gl.generateMipmap; both deref _gl, which is
@@ -2154,17 +3149,31 @@ export class ThinNativeEngine extends ThinEngine {
2154
3149
  // resolved (the same way 2D RTTs get their mips here -- unBindFramebuffer issues no explicit mipgen),
2155
3150
  // so this is a no-op on Native.
2156
3151
  }
2157
- bindAttachments(_attachments) {
2158
- // No-op on Native: bgfx renders to every color attachment of the bound framebuffer, so there is
2159
- // no gl.drawBuffers equivalent to select a subset.
3152
+ bindAttachments(attachments) {
3153
+ // bgfx has no gl.drawBuffers equivalent, so draw calls always write to every color attachment of
3154
+ // the bound framebuffer. Clears, however, can be masked per attachment (bgfx clear color palette),
3155
+ // and code such as PrePassRenderer._clear() / ThinDepthPeelingRenderer relies on that.
3156
+ //
3157
+ // attachments[i] is the attachment index to enable, or -1 to skip (see buildTextureLayout).
3158
+ // Do NOT promote "every entry in this array is enabled" to 0xff: OIT passes layout [0] (only
3159
+ // the depth attachment) as a length-1 array, and treating that as "all attachments" would
3160
+ // clear the shared front/back color MRTs with the depth clear value (-99999).
3161
+ let mask = 0;
3162
+ for (let i = 0; i < attachments.length; i++) {
3163
+ const attachmentIndex = attachments[i];
3164
+ if (attachmentIndex >= 0) {
3165
+ mask |= 1 << attachmentIndex;
3166
+ }
3167
+ }
3168
+ // An empty layout disables color clears; it must not clear every attachment instead.
3169
+ this._clearAttachmentMask = mask;
2160
3170
  }
2161
3171
  clearAttachments(color, attachments, clearColor, clearDepth, clearStencil = false, stencilClearValue = 0) {
2162
3172
  this.bindAttachments(attachments);
2163
3173
  this.clear(color, clearColor, clearDepth, clearStencil, stencilClearValue);
2164
3174
  }
2165
3175
  buildTextureLayout(textureStatus, _backBufferLayout = false) {
2166
- // Native has no gl draw-buffer enums; return a per-attachment index list (consumers only use the
2167
- // length/order, and bindAttachments is a no-op).
3176
+ // Native layouts use attachment indices instead of WebGL draw-buffer enums.
2168
3177
  const result = [];
2169
3178
  for (let i = 0; i < textureStatus.length; i++) {
2170
3179
  result.push(textureStatus[i] ? i : -1);
@@ -2172,10 +3181,12 @@ export class ThinNativeEngine extends ThinEngine {
2172
3181
  return result;
2173
3182
  }
2174
3183
  restoreSingleAttachment() {
2175
- // No-op on Native (see bindAttachments).
3184
+ // Back to the single-attachment back buffer: no masking (see bindAttachments).
3185
+ this._clearAttachmentMask = _AllAttachmentsMask;
2176
3186
  }
2177
3187
  restoreSingleAttachmentForRenderTarget() {
2178
- // No-op on Native (see bindAttachments).
3188
+ // Back to a single-attachment render target: no masking (see bindAttachments).
3189
+ this._clearAttachmentMask = _AllAttachmentsMask;
2179
3190
  }
2180
3191
  generateMipMapsMultiFramebuffer(_texture) {
2181
3192
  // No-op on Native: bgfx auto-generates mips on render-target resolve (as for 2D/cube RTTs).
@@ -2199,26 +3210,42 @@ export class ThinNativeEngine extends ThinEngine {
2199
3210
  return rtWrapper.samples;
2200
3211
  }
2201
3212
  const nativeRTWrapper = rtWrapper;
3213
+ const depthTexture = nativeRTWrapper._depthStencilTexture;
3214
+ if (depthTexture?._hardwareTexture && depthTexture.samples !== samples && depthTexture._references > 1) {
3215
+ throw new Error("updateMultipleRenderTargetTextureSampleCount: cannot change samples while using a shared depth texture.");
3216
+ }
2202
3217
  // bgfx couples MSAA to the texture creation flags (see updateRenderTargetTextureSampleCount), so
2203
3218
  // changing the sample count after the fact requires reissuing every color attachment's underlying
2204
3219
  // bgfx handle with the new MSAA flag. initializeTexture disposes the old handle and allocates a fresh
2205
3220
  // one while preserving the InternalTexture / Graphics::Texture identity; only the internal bgfx handle
2206
3221
  // rotates. Afterwards the framebuffer is recreated so its attachment list refers to the new handles.
3222
+ const reinitialized = new Set();
2207
3223
  for (const texture of textures) {
2208
3224
  // Wrapped (External-source) textures own an opaque external handle (format=-1); reinitializing
2209
3225
  // would destroy it and getNativeTextureFormat would throw, so leave those attachments untouched.
2210
- if (!texture?._hardwareTexture || texture.source === 15 /* InternalTextureSource.External */) {
3226
+ // A texture shared by several attachments (different layers/faces) is reinitialized only once.
3227
+ if (!texture?._hardwareTexture || texture.source === 15 /* InternalTextureSource.External */ || reinitialized.has(texture)) {
2211
3228
  continue;
2212
3229
  }
3230
+ reinitialized.add(texture);
3231
+ // Keep the full descriptor so cube / 2D-array / 3D attachments keep their dimensionality.
2213
3232
  const nativeTexture = texture._hardwareTexture.underlyingResource;
2214
- // See the bgfx-msaa-mips workaround in updateRenderTargetTextureSampleCount (BabylonNative#1714).
2215
- const hasMips = samples > 1 ? false : texture.generateMipMaps;
3233
+ const hasMips = this._getRenderTargetHasMips(this._hasMipStorage(texture), samples);
2216
3234
  this._engine.initializeTexture(nativeTexture, texture.baseWidth, texture.baseHeight, hasMips, getNativeTextureFormat(texture.format, texture.type),
2217
- /*renderTarget*/ true, texture._useSRGBBuffer, samples);
3235
+ /*renderTarget*/ true, texture._useSRGBBuffer, samples, texture.isCube, texture.is2DArray || texture.is3D ? texture.depth : 0, texture.is3D);
2218
3236
  texture.samples = samples;
2219
3237
  }
2220
3238
  nativeRTWrapper._samples = samples;
2221
- this._createMultiRenderTargetFramebuffer(nativeRTWrapper);
3239
+ if (depthTexture?._hardwareTexture && depthTexture.samples !== samples) {
3240
+ this._recreateDepthStencilTexture(nativeRTWrapper, samples);
3241
+ }
3242
+ if (nativeRTWrapper._usesLayeredFramebuffer) {
3243
+ // Rebuilt on the next bind with the per-attachment layers/faces (the layout key includes samples).
3244
+ nativeRTWrapper._invalidateLayeredFramebuffer();
3245
+ }
3246
+ else {
3247
+ this._createMultiRenderTargetFramebuffer(nativeRTWrapper);
3248
+ }
2222
3249
  return samples;
2223
3250
  }
2224
3251
  updateRenderTargetTextureSampleCount(rtWrapper, samples) {
@@ -2237,6 +3264,10 @@ export class ThinNativeEngine extends ThinEngine {
2237
3264
  throw new Error("updateRenderTargetTextureSampleCount: changing MSAA samples is not supported on wrapped (External-source) textures. Dispose and re-wrap with the desired samples.");
2238
3265
  }
2239
3266
  const nativeRTWrapper = rtWrapper;
3267
+ const depthTexture = nativeRTWrapper._depthStencilTexture;
3268
+ if (depthTexture?._hardwareTexture && depthTexture.samples !== samples && depthTexture._references > 1) {
3269
+ throw new Error("updateRenderTargetTextureSampleCount: cannot change samples while using a shared depth texture.");
3270
+ }
2240
3271
  const nativeTexture = texture._hardwareTexture.underlyingResource;
2241
3272
  // bgfx couples MSAA to the texture creation flags, so changing samples after the fact requires
2242
3273
  // recreating the underlying bgfx texture handle with the new MSAA flag. initializeTexture on the
@@ -2245,41 +3276,40 @@ export class ThinNativeEngine extends ThinEngine {
2245
3276
  // only the internal bgfx handle rotates. After the texture is reissued we also recreate the
2246
3277
  // framebuffer so its attachment list refers to the new handle.
2247
3278
  //
2248
- // TODO(bgfx-msaa-mips): stopgap workaround for a bgfx bug. D3D11 forbids MipLevels > 1 on
2249
- // multisampled textures (E_INVALIDARG on CreateTexture2D); D3D12/Vulkan have equivalent rules. bgfx's
2250
- // D3D11 backend uses one D3D11_TEXTURE2D_DESC for both the MSAA render texture (m_rt2d) and the
2251
- // non-MSAA sample target (m_texture2d) and doesn't reset desc.MipLevels between them -- so requesting
2252
- // samples > 1 with hasMips = true crashes at m_rt2d creation. The trigger in practice is the glTF
2253
- // transmission helper, which creates an `opaqueSceneTexture` RTT with generateMipmaps: true and
2254
- // immediately sets samples = 4. WebGL2 sidesteps this with a separate non-mipped multisample
2255
- // renderbuffer; bgfx D3D11 conflates the two-stage pattern. The proper fix lives in bgfx (per-backend
2256
- // patches reset MipLevels=1 for the MSAA target). Tracked in BabylonNative#1714. Until that ships
2257
- // through bgfx -> bgfx.cmake -> BabylonNative -> stable npm, force hasMips = false here so the bad
2258
- // combo never reaches bgfx. Cost: MSAA RTs on Native lose post-resolve auto-mipgen and diverge from
2259
- // WebGL/WebGPU semantics -- texture.generateMipMaps still reads true on the JS side, but the
2260
- // underlying bgfx resource has 1 mip level. Remove this guard once a fixed bgfx is in stable BN npm.
2261
- const hasMips = samples > 1 ? false : texture.generateMipMaps;
3279
+ const hasMips = this._getRenderTargetHasMips(this._hasMipStorage(texture), samples);
2262
3280
  const nativeTextureFormat = getNativeTextureFormat(texture.format, texture.type);
2263
3281
  const isCube = texture.isCube;
3282
+ const is3D = texture.is3D;
3283
+ const layers = texture.is2DArray || is3D ? texture.depth : 0;
3284
+ // Lazily-built per-(mip, layer/face) framebuffers reference the texture about to be recreated.
3285
+ nativeRTWrapper._releaseLayerFramebuffers();
2264
3286
  this._engine.initializeTexture(nativeTexture, texture.baseWidth, texture.baseHeight, hasMips, nativeTextureFormat,
2265
- /*renderTarget*/ true, texture._useSRGBBuffer, samples, isCube);
2266
- if (isCube) {
2267
- // Cube RTTs render through one framebuffer per face (see createRenderTargetCubeTexture). The
2268
- // underlying bgfx handle was just rotated, so recreate all six attachments; the _framebuffers
2269
- // setter releases the stale ones and keeps _framebuffer aliased to face 0 for single-target paths.
2270
- const framebuffers = [];
2271
- for (let face = 0; face < 6; face++) {
2272
- framebuffers.push(this._engine.createFrameBuffer(nativeTexture, texture.baseWidth, texture.baseHeight, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer, samples, face));
3287
+ /*renderTarget*/ true, texture._useSRGBBuffer, samples, isCube, layers, is3D);
3288
+ rtWrapper._samples = samples;
3289
+ texture.samples = samples;
3290
+ const hasDepthStencilTexture = !!rtWrapper._depthStencilTexture?._hardwareTexture;
3291
+ if (is3D) {
3292
+ // 3D render targets render through the per-(mip, layer) framebuffers released above.
3293
+ if (hasDepthStencilTexture) {
3294
+ this._recreateDepthStencilTexture(nativeRTWrapper, samples);
2273
3295
  }
2274
- nativeRTWrapper._framebuffers = framebuffers;
3296
+ return samples;
3297
+ }
3298
+ if (layers > 0 || isCube) {
3299
+ // 2D-array RTTs render through one framebuffer per layer and cube RTTs through one per face (see
3300
+ // createRenderTargetTexture / createRenderTargetCubeTexture). The _framebuffers setter releases the stale
3301
+ // handles and keeps _framebuffer aliased to the first one for single-target paths.
3302
+ nativeRTWrapper._framebuffers = this._createLayerFramebuffers(nativeTexture, texture.baseWidth, texture.baseHeight, isCube ? 6 : layers, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer && !hasDepthStencilTexture, samples, texture.generateMipMaps);
2275
3303
  }
2276
3304
  else {
2277
3305
  // NativeRenderTargetWrapper._framebuffer setter releases the old framebuffer before assigning,
2278
3306
  // so no manual _releaseFramebufferObjects call is needed (and would double-delete the handle).
2279
3307
  nativeRTWrapper._framebuffer = this._engine.createFrameBuffer(nativeTexture, texture.baseWidth, texture.baseHeight, rtWrapper._generateStencilBuffer, rtWrapper._generateDepthBuffer, samples);
2280
3308
  }
2281
- rtWrapper._samples = samples;
2282
- texture.samples = samples;
3309
+ // A sampleable depth texture must match the new sample count and stay attached to every framebuffer.
3310
+ if (hasDepthStencilTexture) {
3311
+ this._recreateDepthStencilTexture(nativeRTWrapper, samples);
3312
+ }
2283
3313
  return samples;
2284
3314
  }
2285
3315
  updateTextureSamplingMode(samplingMode, texture) {
@@ -2289,18 +3319,75 @@ export class ThinNativeEngine extends ThinEngine {
2289
3319
  }
2290
3320
  texture.samplingMode = samplingMode;
2291
3321
  }
2292
- bindFramebuffer(texture, faceIndex, requiredWidth, requiredHeight, forceFullscreenViewport) {
3322
+ updateTextureWrappingMode(texture, wrapU, wrapV = null, wrapR = null) {
3323
+ if (wrapU !== null) {
3324
+ texture._cachedWrapU = wrapU;
3325
+ }
3326
+ if (wrapV !== null) {
3327
+ texture._cachedWrapV = wrapV;
3328
+ }
3329
+ if ((texture.is2DArray || texture.is3D) && wrapR !== null) {
3330
+ texture._cachedWrapR = wrapR;
3331
+ }
3332
+ // Apply to the native texture too; axes that were not specified keep their cached (or default) mode.
3333
+ const nativeTexture = texture._hardwareTexture?.underlyingResource;
3334
+ if (nativeTexture) {
3335
+ this._setTextureWrapMode(nativeTexture, getNativeAddressMode(texture._cachedWrapU ?? 1), getNativeAddressMode(texture._cachedWrapV ?? 1), getNativeAddressMode(texture._cachedWrapR ?? 1));
3336
+ }
3337
+ }
3338
+ bindFramebuffer(texture, faceIndex, requiredWidth, requiredHeight, forceFullscreenViewport, lodLevel, layer) {
2293
3339
  const nativeRTWrapper = texture;
2294
3340
  if (this._currentRenderTarget) {
2295
3341
  this.unBindFramebuffer(this._currentRenderTarget);
2296
3342
  }
2297
3343
  this._currentRenderTarget = texture;
3344
+ // Multi render target whose color attachments each target a specific layer/face of a texture: the IBL
3345
+ // voxelization MRTs (several Z-slices of one shared 3D texture, routed via is3D) and mixed-type MRTs
3346
+ // (2D-array layer / cube face / 2D attachments, routed via _isMixedTypeMRT). The color textures may be
3347
+ // swapped in via setInternalTexture after creation, so (re)build the layered multi-attachment
3348
+ // framebuffer here from the current textures + their per-attachment layer/face indices.
3349
+ if (nativeRTWrapper.isMulti && (nativeRTWrapper.is3D || nativeRTWrapper._isMixedTypeMRT || this._isLayeredFrameGraphMRT(nativeRTWrapper))) {
3350
+ this._bindLayeredMultiFramebuffer(nativeRTWrapper);
3351
+ this._applyBoundViewport(forceFullscreenViewport);
3352
+ return;
3353
+ }
3354
+ // Single 3D render target (IBL voxel grid + its procedural mip chain): render to the requested
3355
+ // (mip, layer) through a lazily-built, cached per-slice framebuffer. requiredWidth/Height carry the
3356
+ // mip dimensions for the voxel mip-copy pass (forceFullscreenViewport is always set by that caller).
3357
+ if (nativeRTWrapper.is3D) {
3358
+ this._bindUnboundFramebuffer(this._get3DLayerFramebuffer(nativeRTWrapper, lodLevel ?? 0, layer ?? 0, requiredWidth, requiredHeight));
3359
+ this._applyBoundViewport(forceFullscreenViewport);
3360
+ return;
3361
+ }
2298
3362
  if (requiredWidth || requiredHeight) {
2299
3363
  throw new Error("Required width/height for frame buffers not yet supported in NativeEngine.");
2300
3364
  }
3365
+ // Frame graph render targets are created via createMultipleRenderTarget({ dontCreateTextures: true }),
3366
+ // so no bgfx framebuffer is built up-front; the externally-allocated color/depth textures are attached
3367
+ // afterwards via setTexture/setDepthStencilTexture. Lazily build a framebuffer from those textures the
3368
+ // first time the wrapper is bound. Several wrappers can reference the same underlying texture(s), so the
3369
+ // framebuffer is cached on (and shared through) the first color texture's hardware wrapper to avoid each
3370
+ // fresh framebuffer/view clearing the texture and clobbering earlier passes.
3371
+ if (!nativeRTWrapper._framebuffers && !nativeRTWrapper._framebufferDepthStencil && !nativeRTWrapper._framebuffer) {
3372
+ this._buildFrameGraphFramebuffer(nativeRTWrapper);
3373
+ }
2301
3374
  if (nativeRTWrapper._framebuffers) {
2302
- // Cube render target: bind the framebuffer for the requested face.
2303
- this._bindUnboundFramebuffer(nativeRTWrapper._framebuffers[faceIndex ?? 0]);
3375
+ // _framebuffers is indexed by cube face for cube render targets, but by array layer for 2D-array
3376
+ // render targets (cascaded shadow maps, the atmosphere aerial-perspective LUT). Callers pass the
3377
+ // face in `faceIndex` and the array slice in `layer`, so pick whichever applies to this wrapper;
3378
+ // indexing a layered target by `faceIndex` bound slice 0 for every layer and left slices 1..N-1
3379
+ // unwritten.
3380
+ const isCubeTarget = nativeRTWrapper.isCube;
3381
+ const framebufferIndex = isCubeTarget ? (faceIndex ?? 0) : layer || faceIndex || 0;
3382
+ // Cube render target: bind the framebuffer for the requested face. HDR prefiltering renders each
3383
+ // roughness level into its own mip, so for lodLevel > 0 lazily build/cache a per-(face, mip)
3384
+ // framebuffer; the pre-built _framebuffers array only targets mip 0.
3385
+ if (lodLevel && isCubeTarget) {
3386
+ this._bindUnboundFramebuffer(this._getCubeFaceMipFramebuffer(nativeRTWrapper, faceIndex ?? 0, lodLevel));
3387
+ }
3388
+ else {
3389
+ this._bindUnboundFramebuffer(nativeRTWrapper._framebuffers[Math.min(framebufferIndex, nativeRTWrapper._framebuffers.length - 1)]);
3390
+ }
2304
3391
  }
2305
3392
  else if (faceIndex) {
2306
3393
  throw new Error("Cuboid frame buffers are not yet supported in NativeEngine.");
@@ -2311,6 +3398,156 @@ export class ThinNativeEngine extends ThinEngine {
2311
3398
  else {
2312
3399
  this._bindUnboundFramebuffer(nativeRTWrapper._framebuffer);
2313
3400
  }
3401
+ // Match ThinEngine: re-apply the cached viewport (or the fullscreen override) after a framebuffer bind.
3402
+ this._applyBoundViewport(forceFullscreenViewport);
3403
+ }
3404
+ // Re-apply the engine's cached viewport onto the newly bound framebuffer (WebGL/WebGPU behaviour).
3405
+ // forceFullscreenViewport paints 0..1 without clobbering _cachedViewport, matching ThinEngine's
3406
+ // use of _viewport() for the fullscreen path.
3407
+ _applyBoundViewport(forceFullscreenViewport) {
3408
+ if (this._cachedViewport && !forceFullscreenViewport) {
3409
+ this.setViewport(this._cachedViewport);
3410
+ return;
3411
+ }
3412
+ if (forceFullscreenViewport) {
3413
+ const cached = this._cachedViewport;
3414
+ this.setViewport(_FullscreenViewport);
3415
+ this._cachedViewport = cached;
3416
+ }
3417
+ }
3418
+ // Returns (building + caching on first use) the bgfx framebuffer that targets a single (mip, layer)
3419
+ // slice of a 3D render-target texture. Used by the IBL voxel grid + procedural mip chain, whose
3420
+ // ProceduralTexture / mip-copy passes render one Z-slice at a time via bindFramebuffer(lodLevel, layer).
3421
+ _get3DLayerFramebuffer(nativeRTWrapper, mip, layer, requiredWidth, requiredHeight) {
3422
+ if (!nativeRTWrapper._layerFramebuffers) {
3423
+ nativeRTWrapper._layerFramebuffers = new Map();
3424
+ }
3425
+ const key = mip * nativeRTWrapper.depth + layer;
3426
+ let framebuffer = nativeRTWrapper._layerFramebuffers.get(key);
3427
+ if (!framebuffer) {
3428
+ const nativeTexture = nativeRTWrapper.texture._hardwareTexture.underlyingResource;
3429
+ const depthTexture = nativeRTWrapper._depthStencilTexture;
3430
+ const nativeDepth = depthTexture?._hardwareTexture?.underlyingResource;
3431
+ const width = requiredWidth || Math.max(1, nativeRTWrapper.width >> mip);
3432
+ const height = requiredHeight || Math.max(1, nativeRTWrapper.height >> mip);
3433
+ if (nativeDepth) {
3434
+ this._assertVolumeDepthAttachmentSupported();
3435
+ // createMultiFrameBuffer has no mip selector and the explicit depth texture only covers mip 0.
3436
+ if (mip !== 0) {
3437
+ throw new Error("NativeEngine: explicit depth attachments on volume render targets only support rendering to mip 0.");
3438
+ }
3439
+ // generateDepth must be set so a depth texture without a native handle yet (created directly on this
3440
+ // volume) is allocated and aliased by the first slice; later slices borrow the existing handle.
3441
+ framebuffer = this._engine.createMultiFrameBuffer([nativeTexture], width, height, HasStencilAspect(depthTexture.format), true, nativeRTWrapper.samples, [layer], nativeDepth);
3442
+ this._setTextureSampling(nativeDepth, getNativeSamplingMode(depthTexture.samplingMode));
3443
+ this.updateTextureComparisonFunction(depthTexture, depthTexture._comparisonFunction);
3444
+ }
3445
+ else {
3446
+ framebuffer = this._engine.createFrameBuffer(nativeTexture, width, height, nativeRTWrapper._generateStencilBuffer, nativeRTWrapper._generateDepthBuffer, nativeRTWrapper.samples, layer, mip);
3447
+ }
3448
+ nativeRTWrapper._layerFramebuffers.set(key, framebuffer);
3449
+ }
3450
+ return framebuffer;
3451
+ }
3452
+ // Returns (building + caching on first use) the bgfx framebuffer that targets a single (face, mip) of a
3453
+ // cube render-target texture. Used by HDR radiance/irradiance prefiltering, whose face×mip loop renders
3454
+ // an increasingly-rough convolution of the environment into each mip via bindFramebuffer(faceIndex, lod).
3455
+ // The pre-built _framebuffers array only covers mip 0, so mips > 0 are built here on demand.
3456
+ _getCubeFaceMipFramebuffer(nativeRTWrapper, face, mip) {
3457
+ // An explicit (sampleable) depth texture only covers mip 0 and native framebuffers cannot select a depth mip,
3458
+ // so a per-mip framebuffer would silently render against unrelated depth instead of the attached texture.
3459
+ if (nativeRTWrapper._depthStencilTexture) {
3460
+ throw new Error("NativeEngine: explicit depth attachments on cube render targets only support rendering to mip 0.");
3461
+ }
3462
+ if (!nativeRTWrapper._layerFramebuffers) {
3463
+ nativeRTWrapper._layerFramebuffers = new Map();
3464
+ }
3465
+ const key = mip * 6 + face;
3466
+ let framebuffer = nativeRTWrapper._layerFramebuffers.get(key);
3467
+ if (!framebuffer) {
3468
+ const nativeTexture = nativeRTWrapper.texture._hardwareTexture.underlyingResource;
3469
+ const width = Math.max(1, nativeRTWrapper.width >> mip);
3470
+ const height = Math.max(1, nativeRTWrapper.height >> mip);
3471
+ framebuffer = this._engine.createFrameBuffer(nativeTexture, width, height, nativeRTWrapper._generateStencilBuffer, nativeRTWrapper._generateDepthBuffer, nativeRTWrapper.samples, face, mip, nativeRTWrapper.texture.generateMipMaps);
3472
+ nativeRTWrapper._layerFramebuffers.set(key, framebuffer);
3473
+ }
3474
+ return framebuffer;
3475
+ }
3476
+ // A frame-graph multi-render-target wrapper is created via createMultipleRenderTarget({dontCreateTextures:
3477
+ // true}) WITHOUT targetTypes/layerIndex/faceIndex, so it is never flagged _isMixedTypeMRT at creation: its
3478
+ // color textures (setTexture) and per-attachment layer/face indices (setLayerAndFaceIndex, from the render
3479
+ // pass's setOutputLayerAndFaceIndices) are assigned afterwards. Detect the layered case at bind time so it
3480
+ // routes through _bindLayeredMultiFramebuffer (which renders each draw buffer into the correct 2D-array
3481
+ // layer / cube face) instead of the flat _buildFrameGraphFramebuffer (which would bind layer 0 of every
3482
+ // attachment and duplicate a shared array/cube resource, leaving the extra targets unwritten).
3483
+ _isLayeredFrameGraphMRT(wrapper) {
3484
+ const textures = wrapper.textures;
3485
+ if (!textures || textures.length === 0) {
3486
+ return false;
3487
+ }
3488
+ for (const tex of textures) {
3489
+ if (tex && (tex.isCube || tex.is2DArray || tex.is3D)) {
3490
+ return true;
3491
+ }
3492
+ }
3493
+ const faceIndices = wrapper.faceIndices;
3494
+ if (faceIndices) {
3495
+ for (const face of faceIndices) {
3496
+ if (face) {
3497
+ return true;
3498
+ }
3499
+ }
3500
+ }
3501
+ const layerIndices = wrapper.layerIndices;
3502
+ if (layerIndices) {
3503
+ for (const layer of layerIndices) {
3504
+ if (layer) {
3505
+ return true;
3506
+ }
3507
+ }
3508
+ }
3509
+ return false;
3510
+ }
3511
+ // (Re)builds and binds the multi-attachment framebuffer for a layered MRT: either an IBL voxelization MRT
3512
+ // (color attachments are distinct Z-slices of one shared 3D texture) or a mixed-type MRT (color attachments
3513
+ // are specific layers of 2D-array textures and/or faces of cube textures, plus plain 2D). Textures may be
3514
+ // assigned via setInternalTexture and layers/faces retargeted via setLayerAndFaceIndex after creation, so the
3515
+ // framebuffer is built lazily here and rebuilt whenever its attachment layout changes.
3516
+ _bindLayeredMultiFramebuffer(nativeRTWrapper) {
3517
+ const textures = nativeRTWrapper.textures;
3518
+ const layerIndices = nativeRTWrapper.layerIndices;
3519
+ const expectedCount = nativeRTWrapper._attachments?.length ?? textures?.length ?? 0;
3520
+ if (!textures || textures.length < expectedCount) {
3521
+ throw new Error("NativeEngine: cannot bind a layered MRT until every color attachment is assigned.");
3522
+ }
3523
+ const depth = nativeRTWrapper._depthStencilTexture?._hardwareTexture?.underlyingResource;
3524
+ const samples = nativeRTWrapper.samples;
3525
+ const key = nativeRTWrapper._layeredFramebufferKey;
3526
+ let upToDate = !!nativeRTWrapper._framebuffer && !!key && key.depth === depth && key.samples === samples && key.colors.length === expectedCount;
3527
+ for (let i = 0; i < expectedCount; i++) {
3528
+ const tex = textures[i];
3529
+ const nativeTexture = tex?._hardwareTexture?.underlyingResource;
3530
+ if (!nativeTexture) {
3531
+ throw new Error("NativeEngine: cannot bind a layered MRT until every color attachment is assigned.");
3532
+ }
3533
+ const attachmentLayer = tex.is3D ? (layerIndices?.[i] ?? i) : nativeRTWrapper.getBaseArrayLayer(i);
3534
+ if (upToDate && (key.colors[i] !== nativeTexture || key.layers[i] !== attachmentLayer)) {
3535
+ upToDate = false;
3536
+ }
3537
+ }
3538
+ if (!upToDate) {
3539
+ const colorTextures = new Array(expectedCount);
3540
+ const layers = new Array(expectedCount);
3541
+ for (let i = 0; i < expectedCount; i++) {
3542
+ const tex = textures[i];
3543
+ colorTextures[i] = tex._hardwareTexture.underlyingResource;
3544
+ layers[i] = tex.is3D ? (layerIndices?.[i] ?? i) : nativeRTWrapper.getBaseArrayLayer(i);
3545
+ }
3546
+ nativeRTWrapper._framebuffer = this._createNativeMultiFrameBuffer(colorTextures, nativeRTWrapper.width, nativeRTWrapper.height, this._getDepthAttachmentStencil(nativeRTWrapper), !!depth || nativeRTWrapper._generateDepthBuffer, samples, layers, depth);
3547
+ nativeRTWrapper._layeredFramebufferKey = { colors: colorTextures, layers, depth, samples };
3548
+ this._applyDepthTextureSampling(nativeRTWrapper);
3549
+ }
3550
+ this._bindUnboundFramebuffer(nativeRTWrapper._framebuffer);
2314
3551
  }
2315
3552
  unBindFramebuffer(texture, disableGenerateMipMaps = false, onBeforeUnbind) {
2316
3553
  // NOTE: Disabling mipmap generation is not yet supported in NativeEngine.
@@ -2450,6 +3687,14 @@ export class ThinNativeEngine extends ThinEngine {
2450
3687
  this._unsetNativeTexture(uniform);
2451
3688
  }
2452
3689
  }
3690
+ /**
3691
+ * @internal
3692
+ */
3693
+ _bindTextureDirectly(_target, _texture, _forTextureDataUpdate = false, _force = false) {
3694
+ // Generic texture loaders use this WebGL-shaped method around direct uploads. Native upload commands
3695
+ // already receive the destination texture explicitly, so there is no bind operation to perform.
3696
+ return false;
3697
+ }
2453
3698
  /**
2454
3699
  * Unbind all textures
2455
3700
  */
@@ -2530,9 +3775,10 @@ export class ThinNativeEngine extends ThinEngine {
2530
3775
  }
2531
3776
  // bgfx updates the requested sub-rectangle of the existing texture (faceIndex selects the cube
2532
3777
  // face / array layer, lod selects the mip level). invertY is forwarded so the native side can match
2533
- // the vertical orientation the base texture upload uses. Mip regeneration after a partial update is
2534
- // not supported on Native, so generateMipMaps is ignored (consistent with the other raw-texture paths).
2535
- this._engine.updateTextureData(texture._hardwareTexture.underlyingResource, imageData, xOffset, yOffset, width, height, faceIndex, lod, texture.invertY);
3778
+ // the vertical orientation the base texture upload uses. Native rebuilds mips from a full level only, so
3779
+ // generateMipMaps is ignored for partial updates.
3780
+ const fullLevel = xOffset === 0 && yOffset === 0 && width === Math.max(1, texture.width >> lod) && height === Math.max(1, texture.height >> lod);
3781
+ this._engine.updateTextureData(texture._hardwareTexture.underlyingResource, imageData, xOffset, yOffset, width, height, faceIndex, lod, texture.invertY, generateMipMaps && fullLevel);
2536
3782
  }
2537
3783
  /**
2538
3784
  * @internal
@@ -2544,7 +3790,27 @@ export class ThinNativeEngine extends ThinEngine {
2544
3790
  * @internal
2545
3791
  */
2546
3792
  _uploadDataToTextureDirectly(texture, imageData, faceIndex = 0, lod = 0) {
2547
- throw new Error("_uploadDataToTextureDirectly not implemented.");
3793
+ if (!texture._hardwareTexture) {
3794
+ return;
3795
+ }
3796
+ const underlyingResource = texture._hardwareTexture.underlyingResource;
3797
+ // Per-mip uploads (e.g. KTX2 decoded mip levels, called with that mip's dimensions in texture.width/height)
3798
+ // update only the requested level of the texture allocated by the lod 0 upload. Older runtimes without
3799
+ // updateTextureData cannot preserve authored mip content, so fail the load instead of silently
3800
+ // substituting a chain generated from lod 0.
3801
+ if (lod > 0) {
3802
+ if (!this._engine.updateTextureData) {
3803
+ throw new Error("NativeEngine._uploadDataToTextureDirectly: uploading mip levels above 0 requires a Babylon Native runtime with updateTextureData.");
3804
+ }
3805
+ this._engine.updateTextureData(underlyingResource, imageData, 0, 0, texture.width, texture.height, faceIndex, lod, texture.invertY, false);
3806
+ return;
3807
+ }
3808
+ texture.baseWidth = texture.width;
3809
+ texture.baseHeight = texture.height;
3810
+ // Upload raw pixel data (e.g. the decoded IES profile: a single-channel FLOAT texture) into the
3811
+ // native texture. Mirrors updateRawTexture. Like texImage2D, Native reads only the texels it needs from a
3812
+ // larger buffer.
3813
+ this._engine.loadRawTexture(underlyingResource, imageData, texture.width, texture.height, getNativeTextureFormat(texture.format, texture.type), texture.generateMipMaps, texture.invertY, texture._useSRGBBuffer);
2548
3814
  }
2549
3815
  /**
2550
3816
  * @internal
@@ -2563,15 +3829,16 @@ export class ThinNativeEngine extends ThinEngine {
2563
3829
  flushFramebuffer() { }
2564
3830
  // eslint-disable-next-line @typescript-eslint/promise-function-async
2565
3831
  _readTexturePixels(texture, width, height, faceIndex, level, buffer, _flushRenderer, _noDataConversion, x, y) {
2566
- if (faceIndex !== undefined && faceIndex !== -1) {
2567
- throw new Error(`Reading cubemap faces is not supported, but faceIndex is ${faceIndex}.`);
3832
+ if (faceIndex !== undefined && faceIndex !== -1 && (faceIndex < 0 || faceIndex > 5)) {
3833
+ throw new Error(`Invalid cubemap face index ${faceIndex}; expected 0-5 or -1.`);
2568
3834
  }
2569
- return (this._engine
2570
- .readTexture(texture._hardwareTexture?.underlyingResource, level ?? 0, x ?? 0, y ?? 0, width, height, buffer?.buffer ?? null, buffer?.byteOffset ?? 0, buffer?.byteLength ?? 0)
3835
+ return ((this._engine.readTexture2 ?? this._engine.readTexture)
3836
+ .call(this._engine, texture._hardwareTexture?.underlyingResource, level ?? 0, x ?? 0, y ?? 0, width, height, buffer?.buffer ?? null, buffer?.byteOffset ?? 0, buffer?.byteLength ?? 0, faceIndex ?? -1)
2571
3837
  // eslint-disable-next-line github/no-then
2572
3838
  .then((rawBuffer) => {
2573
3839
  if (!buffer) {
2574
- buffer = new Uint8Array(rawBuffer);
3840
+ // Legacy readTexture returns RGBA8; readTexture2 preserves float sources as RGBA32F.
3841
+ buffer = rawBuffer.byteLength === 16 * width * height ? new Float32Array(rawBuffer) : new Uint8Array(rawBuffer);
2575
3842
  }
2576
3843
  return buffer;
2577
3844
  }));