@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/README.md +59 -2
  2. package/dist/core.js +36923 -0
  3. package/dist/core.js.map +6 -0
  4. package/dist/index.js +32 -0
  5. package/dist/loaders-gl-worker.js +82 -0
  6. package/dist/loaders-gl-worker.js.map +6 -0
  7. package/dist/map-logic.js +2 -0
  8. package/dist/map-logic.js.map +6 -0
  9. package/dist/projected-basemaps-worker.js +3 -0
  10. package/dist/projected-basemaps-worker.js.map +6 -0
  11. package/dist/shader-modules.js +141 -0
  12. package/dist/shader-modules.js.map +6 -0
  13. package/dist/style-schema.js +126 -0
  14. package/dist/style-schema.js.map +6 -0
  15. package/dist/tangram-style.schema.json +882 -0
  16. package/dist/tangram.debug.mjs +37139 -0
  17. package/dist/tangram.debug.mjs.map +6 -0
  18. package/dist/tangram.min.mjs +4950 -0
  19. package/dist/types/builders/earcut.d.ts +15 -0
  20. package/dist/types/core.d.ts +29 -0
  21. package/dist/types/gl/constants.d.ts +2 -0
  22. package/dist/types/gl/globe_mesh.d.ts +48 -0
  23. package/dist/types/gl/glsl.d.ts +17 -0
  24. package/dist/types/gl/vertex-constants.d.ts +4 -0
  25. package/dist/types/gl/vertex-types.d.ts +34 -0
  26. package/dist/types/gl/vertex_data.d.ts +32 -0
  27. package/dist/types/gl/vertex_elements.d.ts +8 -0
  28. package/dist/types/gl/vertex_layout.d.ts +32 -0
  29. package/dist/types/glsl.d.ts +14 -0
  30. package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
  31. package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
  32. package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
  33. package/dist/types/index.d.ts +155 -0
  34. package/dist/types/lights/light-definitions.d.ts +121 -0
  35. package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
  36. package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
  37. package/dist/types/map-logic/globe-terrain.d.ts +54 -0
  38. package/dist/types/map-logic/index.d.ts +33 -0
  39. package/dist/types/map-logic/label-identity.d.ts +22 -0
  40. package/dist/types/map-logic/label-placement.d.ts +32 -0
  41. package/dist/types/map-logic/screen-bounds.d.ts +15 -0
  42. package/dist/types/map-logic/terrain-data.d.ts +55 -0
  43. package/dist/types/map-logic/terrain-error.d.ts +51 -0
  44. package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
  45. package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
  46. package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
  47. package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
  48. package/dist/types/map-logic/terrain-query.d.ts +33 -0
  49. package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
  50. package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
  51. package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
  52. package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
  53. package/dist/types/map-logic/tile-residency.d.ts +17 -0
  54. package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
  55. package/dist/types/procedures/js-yaml.d.ts +15 -0
  56. package/dist/types/procedures/mesh-projector.d.ts +107 -0
  57. package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
  58. package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
  59. package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
  60. package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
  61. package/dist/types/procedures/terrain-draping.d.ts +165 -0
  62. package/dist/types/procedures/terrain-globe.d.ts +6 -0
  63. package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
  64. package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
  65. package/dist/types/procedures/web-mercator-math.d.ts +4 -0
  66. package/dist/types/scene/globals.d.ts +11 -0
  67. package/dist/types/scene/ground_footprint.d.ts +16 -0
  68. package/dist/types/scene/host_frame.d.ts +54 -0
  69. package/dist/types/scene/projection_math.d.ts +52 -0
  70. package/dist/types/scene/scene-resource-types.d.ts +177 -0
  71. package/dist/types/scene/scene_bundle.d.ts +69 -0
  72. package/dist/types/scene/visibility_adapter.d.ts +106 -0
  73. package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
  74. package/dist/types/selection/terrain_surface.d.ts +149 -0
  75. package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
  76. package/dist/types/shader-modules/height-decode.d.ts +13 -0
  77. package/dist/types/shader-modules/hillshade.d.ts +13 -0
  78. package/dist/types/shader-modules/index.d.ts +6 -0
  79. package/dist/types/shader-modules/planar.d.ts +12 -0
  80. package/dist/types/shader-modules/sphere-map.d.ts +13 -0
  81. package/dist/types/shader-modules/triplanar.d.ts +15 -0
  82. package/dist/types/sources/decoded_tile_store.d.ts +95 -0
  83. package/dist/types/sources/tile_source_metadata.d.ts +32 -0
  84. package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
  85. package/dist/types/tile/tile_id.d.ts +37 -0
  86. package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
  87. package/dist/types/types.d.ts +293 -0
  88. package/dist/types/utils/geo.d.ts +57 -0
  89. package/dist/types/utils/hash.d.ts +1 -0
  90. package/dist/types/utils/log.d.ts +27 -0
  91. package/dist/types/utils/props.d.ts +6 -0
  92. package/dist/types/utils/subscribe.d.ts +10 -0
  93. package/dist/types/utils/thread.d.ts +6 -0
  94. package/dist/types/utils/urls.d.ts +26 -0
  95. package/dist/types/utils/utils.d.ts +26 -0
  96. package/dist/types/utils/version.d.ts +2 -0
  97. package/dist/types/utils/worker-types.d.ts +68 -0
  98. package/dist/types/utils/worker_broker.d.ts +3 -0
  99. package/package.json +103 -3
  100. package/src/builders/common.ts +41 -0
  101. package/src/builders/earcut.d.ts +15 -0
  102. package/src/builders/geometry-types.ts +60 -0
  103. package/src/builders/points.ts +115 -0
  104. package/src/builders/polygons.ts +225 -0
  105. package/src/builders/polylines.ts +682 -0
  106. package/src/builders/wireframe.ts +31 -0
  107. package/src/core.d.ts +29 -0
  108. package/src/core.ts +33 -0
  109. package/src/experimental/loaders-gl-worker.ts +19 -0
  110. package/src/experimental/projected-basemaps-worker.ts +13 -0
  111. package/src/experimental/projected-mesh-cache.ts +137 -0
  112. package/src/experimental/projected-mesh-domain.ts +110 -0
  113. package/src/experimental/projected-mesh-refinement.ts +193 -0
  114. package/src/experimental/projected-mesh.ts +399 -0
  115. package/src/experimental/projected-normal.ts +26 -0
  116. package/src/experimental/projected-symbol-mesh.ts +74 -0
  117. package/src/gl/constants.ts +18 -0
  118. package/src/gl/context.ts +103 -0
  119. package/src/gl/extensions.ts +30 -0
  120. package/src/gl/globe_mesh.ts +285 -0
  121. package/src/gl/glsl.ts +257 -0
  122. package/src/gl/mesh-types.ts +56 -0
  123. package/src/gl/render_state.ts +111 -0
  124. package/src/gl/shader_program.ts +1310 -0
  125. package/src/gl/texture.ts +814 -0
  126. package/src/gl/uniform_buffer.ts +340 -0
  127. package/src/gl/vao.ts +154 -0
  128. package/src/gl/vbo_mesh.ts +349 -0
  129. package/src/gl/vertex-constants.ts +19 -0
  130. package/src/gl/vertex-types.ts +35 -0
  131. package/src/gl/vertex_data.ts +126 -0
  132. package/src/gl/vertex_elements.ts +54 -0
  133. package/src/gl/vertex_layout.ts +241 -0
  134. package/src/glsl.d.ts +14 -0
  135. package/src/gpu/luma_device_renderer.ts +579 -0
  136. package/src/gpu/pixel_readback.ts +51 -0
  137. package/src/gpu/resource_lifecycle.ts +32 -0
  138. package/src/gpu/tangram_gpu_backend.ts +195 -0
  139. package/src/index.d.ts +155 -0
  140. package/src/index.ts +105 -0
  141. package/src/labels/collision-types.ts +94 -0
  142. package/src/labels/collision.ts +273 -0
  143. package/src/labels/collision_grid.ts +61 -0
  144. package/src/labels/intersect.ts +30 -0
  145. package/src/labels/label-types.ts +91 -0
  146. package/src/labels/label.ts +207 -0
  147. package/src/labels/label_line.ts +801 -0
  148. package/src/labels/label_point.ts +131 -0
  149. package/src/labels/main-pass-types.ts +73 -0
  150. package/src/labels/main_pass.ts +221 -0
  151. package/src/labels/point_anchor.ts +100 -0
  152. package/src/labels/point_placement.ts +150 -0
  153. package/src/labels/projected-pass.ts +259 -0
  154. package/src/labels/repeat_group.ts +72 -0
  155. package/src/lights/ambient_light.glsl +18 -0
  156. package/src/lights/directional_light.glsl +39 -0
  157. package/src/lights/geographic-lights.ts +21 -0
  158. package/src/lights/light-definitions.ts +285 -0
  159. package/src/lights/light.ts +590 -0
  160. package/src/lights/lighting-uniforms.ts +80 -0
  161. package/src/lights/lighting-wgsl.ts +116 -0
  162. package/src/lights/material.glsl +237 -0
  163. package/src/lights/material.ts +145 -0
  164. package/src/lights/native-falloff.ts +37 -0
  165. package/src/lights/point_light.glsl +96 -0
  166. package/src/lights/spot_light.glsl +111 -0
  167. package/src/map-logic/draped-terrain-bounds.ts +71 -0
  168. package/src/map-logic/globe-terrain-preparation.ts +217 -0
  169. package/src/map-logic/globe-terrain.ts +296 -0
  170. package/src/map-logic/index.ts +37 -0
  171. package/src/map-logic/label-identity.ts +36 -0
  172. package/src/map-logic/label-placement.ts +117 -0
  173. package/src/map-logic/screen-bounds.ts +34 -0
  174. package/src/map-logic/terrain-data.ts +107 -0
  175. package/src/map-logic/terrain-error.ts +134 -0
  176. package/src/map-logic/terrain-ground-topology.ts +164 -0
  177. package/src/map-logic/terrain-height-field.ts +254 -0
  178. package/src/map-logic/terrain-navigation.ts +50 -0
  179. package/src/map-logic/terrain-neighborhood.ts +43 -0
  180. package/src/map-logic/terrain-query.ts +51 -0
  181. package/src/map-logic/terrain-resource-budget.ts +124 -0
  182. package/src/map-logic/terrain-transition-topology.ts +164 -0
  183. package/src/map-logic/tile-build-queue.ts +110 -0
  184. package/src/map-logic/tile-cache-policy.ts +89 -0
  185. package/src/map-logic/tile-residency.ts +50 -0
  186. package/src/map-logic/triangle-query-index.ts +265 -0
  187. package/src/procedures/js-yaml.d.ts +15 -0
  188. package/src/procedures/mesh-projector.ts +191 -0
  189. package/src/procedures/mlt-loaders.ts +55 -0
  190. package/src/procedures/mvt-dependencies.d.ts +20 -0
  191. package/src/procedures/mvt-legacy.ts +150 -0
  192. package/src/procedures/mvt-loaders.ts +53 -0
  193. package/src/procedures/mvt-parser.ts +48 -0
  194. package/src/procedures/mvt-properties.ts +36 -0
  195. package/src/procedures/mvt-tile-provider.ts +72 -0
  196. package/src/procedures/pmtiles-loader.ts +75 -0
  197. package/src/procedures/projected-coordinate-transform.ts +79 -0
  198. package/src/procedures/projection-batch-executor.ts +140 -0
  199. package/src/procedures/scene-yaml-legacy.ts +16 -0
  200. package/src/procedures/scene-yaml-loaders.ts +10 -0
  201. package/src/procedures/terrain-draping.ts +713 -0
  202. package/src/procedures/terrain-globe.ts +32 -0
  203. package/src/procedures/terrain-styled-topology.ts +184 -0
  204. package/src/procedures/web-mercator-legacy.ts +24 -0
  205. package/src/procedures/web-mercator-math.ts +50 -0
  206. package/src/scene/camera.ts +298 -0
  207. package/src/scene/camera_base.ts +116 -0
  208. package/src/scene/classic_scene.ts +20 -0
  209. package/src/scene/external_camera.ts +101 -0
  210. package/src/scene/globals.ts +120 -0
  211. package/src/scene/ground_footprint.ts +139 -0
  212. package/src/scene/host_frame.ts +326 -0
  213. package/src/scene/projected_tile_lod.ts +125 -0
  214. package/src/scene/projection_math.ts +142 -0
  215. package/src/scene/projection_shaders.ts +183 -0
  216. package/src/scene/renderer.ts +516 -0
  217. package/src/scene/scene-resource-types.ts +174 -0
  218. package/src/scene/scene.ts +2378 -0
  219. package/src/scene/scene_bundle.ts +304 -0
  220. package/src/scene/scene_debug.ts +129 -0
  221. package/src/scene/scene_loader.ts +402 -0
  222. package/src/scene/scene_worker.ts +528 -0
  223. package/src/scene/view.ts +683 -0
  224. package/src/scene/visibility_adapter.ts +319 -0
  225. package/src/scene/worker_url.ts +15 -0
  226. package/src/selection/draped-terrain-surface.ts +377 -0
  227. package/src/selection/selection-types.ts +79 -0
  228. package/src/selection/selection.ts +542 -0
  229. package/src/selection/selection_fragment.glsl +18 -0
  230. package/src/selection/selection_globals.glsl +9 -0
  231. package/src/selection/selection_pixels.ts +59 -0
  232. package/src/selection/selection_vertex.glsl +15 -0
  233. package/src/selection/terrain_surface.ts +298 -0
  234. package/src/shader-modules/globe-horizon.ts +52 -0
  235. package/src/shader-modules/height-decode.ts +41 -0
  236. package/src/shader-modules/hillshade.ts +70 -0
  237. package/src/shader-modules/index.ts +10 -0
  238. package/src/shader-modules/planar.ts +24 -0
  239. package/src/shader-modules/sphere-map.ts +38 -0
  240. package/src/shader-modules/triplanar.ts +45 -0
  241. package/src/sources/aligned_tile_source.ts +122 -0
  242. package/src/sources/data_source.ts +655 -0
  243. package/src/sources/decoded_tile_store.ts +245 -0
  244. package/src/sources/geojson.ts +300 -0
  245. package/src/sources/mvt.ts +247 -0
  246. package/src/sources/raster.ts +341 -0
  247. package/src/sources/shared_tile_source_adapter.ts +117 -0
  248. package/src/sources/sources.ts +10 -0
  249. package/src/sources/tile_source_adapter.ts +84 -0
  250. package/src/sources/tile_source_metadata.ts +65 -0
  251. package/src/sources/tile_source_state.ts +112 -0
  252. package/src/sources/topojson.ts +90 -0
  253. package/src/styles/feature_annotations.ts +16 -0
  254. package/src/styles/filter.ts +190 -0
  255. package/src/styles/geometry-style-types.ts +209 -0
  256. package/src/styles/globe_visibility_wgsl.ts +22 -0
  257. package/src/styles/layer-types.ts +45 -0
  258. package/src/styles/layer.ts +586 -0
  259. package/src/styles/lines/dasharray.ts +49 -0
  260. package/src/styles/lines/lines.ts +707 -0
  261. package/src/styles/lines/lines_wgsl.ts +224 -0
  262. package/src/styles/points/point-runtime-types.ts +125 -0
  263. package/src/styles/points/points.ts +1079 -0
  264. package/src/styles/points/points_fragment.glsl +126 -0
  265. package/src/styles/points/points_vertex.glsl +230 -0
  266. package/src/styles/points/points_wgsl.ts +189 -0
  267. package/src/styles/polygons/polygons.ts +268 -0
  268. package/src/styles/polygons/polygons_fragment.glsl +173 -0
  269. package/src/styles/polygons/polygons_vertex.glsl +213 -0
  270. package/src/styles/polygons/polygons_wgsl.ts +127 -0
  271. package/src/styles/property-types.ts +125 -0
  272. package/src/styles/raster/raster.ts +33 -0
  273. package/src/styles/raster/raster_globals.glsl +46 -0
  274. package/src/styles/style-mixing-types.ts +101 -0
  275. package/src/styles/style-runtime-types.ts +220 -0
  276. package/src/styles/style-schema.ts +133 -0
  277. package/src/styles/style.ts +1033 -0
  278. package/src/styles/style_globals.glsl +69 -0
  279. package/src/styles/style_manager.ts +464 -0
  280. package/src/styles/style_parser.ts +620 -0
  281. package/src/styles/text/font_manager.ts +180 -0
  282. package/src/styles/text/fontfaceobserver.d.ts +10 -0
  283. package/src/styles/text/text-types.ts +220 -0
  284. package/src/styles/text/text.ts +378 -0
  285. package/src/styles/text/text_canvas.ts +773 -0
  286. package/src/styles/text/text_labels.ts +436 -0
  287. package/src/styles/text/text_segments.ts +142 -0
  288. package/src/styles/text/text_settings.ts +228 -0
  289. package/src/styles/text/text_wgsl.ts +159 -0
  290. package/src/styles/text/text_wrap.ts +194 -0
  291. package/src/tile/globe_tile_preload.ts +40 -0
  292. package/src/tile/tangram_tileset_2d.ts +108 -0
  293. package/src/tile/tile-build-types.ts +155 -0
  294. package/src/tile/tile.ts +807 -0
  295. package/src/tile/tile_id.ts +172 -0
  296. package/src/tile/tile_manager.ts +889 -0
  297. package/src/tile/tile_pyramid.ts +119 -0
  298. package/src/tile/tile_resource_cache.ts +91 -0
  299. package/src/tile/tile_traversal_adapter.ts +132 -0
  300. package/src/types.ts +332 -0
  301. package/src/utils/debounce.ts +17 -0
  302. package/src/utils/debug_settings.ts +43 -0
  303. package/src/utils/errors.ts +12 -0
  304. package/src/utils/functions.ts +95 -0
  305. package/src/utils/geo.ts +364 -0
  306. package/src/utils/hash.ts +14 -0
  307. package/src/utils/log.ts +108 -0
  308. package/src/utils/media_capture.ts +193 -0
  309. package/src/utils/merge.ts +44 -0
  310. package/src/utils/obb.ts +134 -0
  311. package/src/utils/props.ts +43 -0
  312. package/src/utils/slice.ts +14 -0
  313. package/src/utils/subscribe.ts +58 -0
  314. package/src/utils/task.ts +154 -0
  315. package/src/utils/thread.ts +26 -0
  316. package/src/utils/urls.ts +223 -0
  317. package/src/utils/utils.ts +310 -0
  318. package/src/utils/vector.ts +197 -0
  319. package/src/utils/version.ts +8 -0
  320. package/src/utils/worker-types.ts +79 -0
  321. package/src/utils/worker_broker.ts +523 -0
@@ -0,0 +1,579 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import { Buffer, Texture } from '@luma.gl/core';
6
+ import type {Binding, Bindings, BufferProps, Device, PrimitiveTopology, RenderPipeline, RenderPipelineParameters,
7
+ RenderPipelineProps, RenderPass, ShaderProps, TextureFormat, TextureFormatDepthStencil, VertexArray} from '@luma.gl/core';
8
+ import type {TangramDrawableMesh, TangramDrawableProgram, TangramGPUBackend, TangramGPUSceneOptions,
9
+ TangramDrawableUniformBlock, TangramMeshBufferOptions, TangramMeshDrawDescriptor, TangramMeshDrawOptions, TangramRenderStateOptions,
10
+ TangramShaderOptions, TangramShaderProgramOptions, TangramTextureOptions, TangramUniformBufferOptions} from './tangram_gpu_backend';
11
+ import {observeGPUResourceDisposal} from './resource_lifecycle';
12
+
13
+ /** Per-topology pipelines indexed by normalized render state. */
14
+ type PipelineStates = Map<PrimitiveTopology, Map<string, RenderPipeline>>;
15
+ /** Resource kinds accepted by luma render-pass bindings, shared without per-draw list allocations. */
16
+ const BINDING_RESOURCE_TAGS: readonly string[] = ['Buffer', 'Texture', 'TextureView', 'ExternalTexture', 'Sampler'];
17
+ /** Buffer-only discriminator for subrange bindings. */
18
+ const BUFFER_RESOURCE_TAGS: readonly string[] = ['Buffer'];
19
+
20
+ /** Distinguish luma depth/stencil formats without casting arbitrary texture formats. */
21
+ function isDepthStencilFormat(format: TextureFormat): format is TextureFormatDepthStencil {
22
+ return format.startsWith('depth') || format.startsWith('stencil');
23
+ }
24
+
25
+ /** Accept host-device luma bindings, including resources from another core bundle copy. */
26
+ function isNativeBinding(resource: object, device: Device): resource is Binding {
27
+ if (isDeviceResource(resource, device, BINDING_RESOURCE_TAGS)) return true;
28
+ return 'buffer' in resource && isDeviceResource(resource.buffer, device, BUFFER_RESOURCE_TAGS) &&
29
+ (!('offset' in resource) || resource.offset === undefined || typeof resource.offset === 'number') &&
30
+ (!('size' in resource) || resource.size === undefined || typeof resource.size === 'number');
31
+ }
32
+
33
+ /** luma's stable Resource tag and device identity do not inspect raw handles or depend on instanceof. */
34
+ function isDeviceResource(resource: unknown, device: Device, tags: readonly string[]): boolean {
35
+ if (!resource || typeof resource !== 'object' || !('device' in resource) || resource.device !== device ||
36
+ !('destroy' in resource) || typeof resource.destroy !== 'function') return false;
37
+ const tag: unknown = Reflect.get(resource, Symbol.toStringTag);
38
+ return typeof tag === 'string' && tags.includes(tag);
39
+ }
40
+
41
+ /** Resource record deliberately contains no reference to its shader-program owner. */
42
+ type ProgramResources = {
43
+ layouts: WeakMap<object, PipelineStates>;
44
+ pipelines: Set<RenderPipeline>;
45
+ disposed: boolean;
46
+ unsubscribe: () => void;
47
+ };
48
+
49
+ /** Cached draw resources, without a strong reference to their mesh owner. */
50
+ type MeshResources = {
51
+ vertexArrays: Map<RenderPipeline, VertexArray>;
52
+ uniformBuffers: Map<string, {buffer: Buffer; byteLength: number}>;
53
+ disposed: boolean;
54
+ unsubscribe: () => void;
55
+ };
56
+
57
+ /**
58
+ * Portable Tangram GPU backend implemented exclusively with the luma.gl Device API.
59
+ *
60
+ * This class deliberately never reads `device.handle`. It owns resources and draw
61
+ * submission while Tangram continues to own tile building and scene traversal.
62
+ *
63
+ * @implements {import('./tangram_gpu_backend').TangramGPUBackend}
64
+ */
65
+ export default class LumaDeviceRenderer implements TangramGPUBackend {
66
+ declare readonly device: Device;
67
+ /** Shader/layout/state cache; keys do not keep scene programs alive. */
68
+ declare private pipeline_cache: WeakMap<TangramDrawableProgram, ProgramResources>;
69
+ /** Mesh/pipeline vertex-array cache. */
70
+ declare private mesh_resource_cache: WeakMap<TangramDrawableMesh, MeshResources>;
71
+ /** Live program generations; released on recompilation/destruction or backend teardown. */
72
+ declare private program_resources: Set<ProgramResources>;
73
+ /** Live mesh resources; released on tile eviction or backend teardown. */
74
+ declare private mesh_resources: Set<MeshResources>;
75
+ /** A destroyed backend cannot reuse cached or host resources. */
76
+ declare private destroyed: boolean;
77
+ /** Restore native types at the capability-only wrapper boundary, without asserting a resource facade. */
78
+ private readonly bindingResources = new WeakMap<object, Buffer | Texture>();
79
+
80
+ constructor(device: Device) {
81
+ validateDevice(device);
82
+ this.device = device;
83
+ this.pipeline_cache = new WeakMap();
84
+ this.mesh_resource_cache = new WeakMap();
85
+ this.program_resources = new Set();
86
+ this.mesh_resources = new Set();
87
+ this.destroyed = false;
88
+ }
89
+
90
+ /** Shader language selected by the host device. */
91
+ get shaderLanguage() {
92
+ return this.device.info.shadingLanguage;
93
+ }
94
+
95
+ /** Maximum supported two-dimensional texture dimension. */
96
+ get maxTextureSize() {
97
+ return this.device.limits && this.device.limits.maxTextureDimension2D;
98
+ }
99
+
100
+ /**
101
+ * Returns the resource factories consumed by Tangram Scene and Style objects.
102
+ */
103
+ getSceneOptions(): TangramGPUSceneOptions {
104
+ this.assertAlive();
105
+ return {
106
+ enableUniformBuffers: true,
107
+ deviceShaderCompilation: true,
108
+ shaderLanguage: this.shaderLanguage,
109
+ uniformBufferFactory: options => this.createUniformBuffer(options),
110
+ shaderFactory: options => this.createShader(options),
111
+ shaderProgramValidator: options => this.validateShaderProgram(options),
112
+ meshBufferFactory: options => this.createMeshBuffer(options),
113
+ textureFactory: options => this.createTexture(options),
114
+ maxTextureSize: this.maxTextureSize,
115
+ meshRenderer: this
116
+ };
117
+ }
118
+
119
+ /** Creates a luma.gl uniform buffer. */
120
+ createUniformBuffer(options: TangramUniformBufferOptions) {
121
+ this.assertAlive();
122
+ if (options.usage !== 'uniform') {
123
+ throw new Error(`unsupported Tangram buffer usage '${options.usage}'`);
124
+ }
125
+ const buffer = this.device.createBuffer({
126
+ id: `tangram-${options.id}`,
127
+ byteLength: options.byteLength,
128
+ usage: Buffer.UNIFORM | Buffer.COPY_DST
129
+ });
130
+ this.bindingResources.set(buffer, buffer);
131
+ return buffer;
132
+ }
133
+
134
+ /** Creates a shader in the language supported by the active device. */
135
+ createShader(options: TangramShaderOptions) {
136
+ this.assertAlive();
137
+ const shader_options: ShaderProps = {
138
+ id: `tangram-${options.id}`,
139
+ language: options.language || this.device.info.shadingLanguage,
140
+ stage: options.stage,
141
+ source: options.source
142
+ };
143
+ if (options.entryPoint) {
144
+ shader_options.entryPoint = options.entryPoint;
145
+ }
146
+ return this.device.createShader(shader_options);
147
+ }
148
+
149
+ /** Validates a device-owned shader pair when the backend supports layout-free linking. */
150
+ validateShaderProgram({ id, vertexShader, fragmentShader }: TangramShaderProgramOptions) {
151
+ this.assertAlive();
152
+ // A WebGL program can only be linked against its concrete vertex
153
+ // layout. Tangram does not know that layout until the first mesh draw,
154
+ // where getPipeline() creates the real pipeline with the required
155
+ // attributes. An empty validation layout incorrectly rejects shaders
156
+ // such as TRON roads that declare style-specific vertex attributes.
157
+ if (this.device.type === 'webgl') {
158
+ return;
159
+ }
160
+ const pipeline = this.device.createRenderPipeline({
161
+ id: `tangram-${id}-validation`,
162
+ vs: vertexShader,
163
+ fs: fragmentShader,
164
+ topology: 'triangle-list',
165
+ bufferLayout: [],
166
+ disableWarnings: true
167
+ });
168
+ try {
169
+ if (pipeline.isErrored) {
170
+ throw new Error(`Tangram shader program '${id}' failed device link validation`);
171
+ }
172
+ }
173
+ finally {
174
+ pipeline.destroy();
175
+ }
176
+ }
177
+
178
+ /** Creates a luma.gl vertex or index buffer. */
179
+ createMeshBuffer(options: TangramMeshBufferOptions) {
180
+ this.assertAlive();
181
+ const usage = options.usage === 'vertex' ? Buffer.VERTEX :
182
+ options.usage === 'index' ? Buffer.INDEX : null;
183
+ if (usage == null) {
184
+ throw new Error(`unsupported Tangram mesh buffer usage '${options.usage}'`);
185
+ }
186
+ const props: BufferProps = {
187
+ id: `tangram-${options.id}`,
188
+ usage: usage | Buffer.COPY_DST,
189
+ data: options.data
190
+ };
191
+ if (options.indexType) {
192
+ props.indexType = options.indexType;
193
+ }
194
+ return this.device.createBuffer(props);
195
+ }
196
+
197
+ /** Creates and initializes a luma.gl texture. */
198
+ createTexture(options: TangramTextureOptions) {
199
+ this.assertAlive();
200
+ const mipmapped = options.filtering === 'mipmap' && this.device.type === 'webgl';
201
+ const filter = options.filtering === 'nearest' ? 'nearest' : 'linear';
202
+ const texture = this.device.createTexture({
203
+ id: `tangram-${options.id}`,
204
+ width: options.width,
205
+ height: options.height,
206
+ format: 'rgba8unorm',
207
+ usage: Texture.COPY_DST | Texture.SAMPLE | Texture.RENDER,
208
+ mipLevels: mipmapped ?
209
+ Math.floor(Math.log2(Math.max(options.width, options.height))) + 1 : 1,
210
+ sampler: {
211
+ minFilter: filter,
212
+ magFilter: filter,
213
+ mipmapFilter: mipmapped ? 'linear' : 'none',
214
+ addressModeU: options.repeat ? 'repeat' : 'clamp-to-edge',
215
+ addressModeV: options.repeat ? 'repeat' : 'clamp-to-edge'
216
+ }
217
+ });
218
+ this.bindingResources.set(texture, texture);
219
+
220
+ try {
221
+ if (options.data != null) {
222
+ if (ArrayBuffer.isView(options.data) || options.data instanceof ArrayBuffer) {
223
+ texture.writeData(options.data, {
224
+ width: options.width,
225
+ height: options.height
226
+ });
227
+ }
228
+ else {
229
+ texture.copyExternalImage({
230
+ image: options.data,
231
+ width: options.width,
232
+ height: options.height,
233
+ flipY: options.flipY,
234
+ premultipliedAlpha: options.premultipliedAlpha
235
+ });
236
+ }
237
+ if (mipmapped) {
238
+ texture.generateMipmapsWebGL();
239
+ }
240
+ }
241
+ return texture;
242
+ }
243
+ catch (error) {
244
+ texture.destroy();
245
+ throw error;
246
+ }
247
+ }
248
+
249
+ /** Translates normalized Tangram state into luma.gl pipeline parameters. */
250
+ getRenderPipelineParameters({ depthTest, depthWrite, cullFace, blend }: TangramRenderStateOptions) {
251
+ const parameters: RenderPipelineParameters = {
252
+ cullMode: cullFace ? 'back' : 'none',
253
+ depthCompare: depthTest ? 'less' : 'always',
254
+ depthWriteEnabled: depthWrite,
255
+ blend: Boolean(blend && blend !== 'opaque')
256
+ };
257
+
258
+ if (blend === 'overlay' || blend === 'inlay' || blend === 'translucent') {
259
+ Object.assign(parameters, {
260
+ blendColorOperation: 'add',
261
+ blendColorSrcFactor: 'src-alpha',
262
+ blendColorDstFactor: 'one-minus-src-alpha',
263
+ blendAlphaOperation: 'add',
264
+ blendAlphaSrcFactor: 'one',
265
+ blendAlphaDstFactor: 'one-minus-src-alpha'
266
+ });
267
+ }
268
+ else if (blend === 'add') {
269
+ Object.assign(parameters, {
270
+ blendColorOperation: 'add',
271
+ blendColorSrcFactor: 'one',
272
+ blendColorDstFactor: 'one',
273
+ blendAlphaOperation: 'add',
274
+ blendAlphaSrcFactor: 'one',
275
+ blendAlphaDstFactor: 'one'
276
+ });
277
+ }
278
+ else if (blend === 'multiply') {
279
+ Object.assign(parameters, {
280
+ blendColorOperation: 'add',
281
+ blendColorSrcFactor: 'zero',
282
+ blendColorDstFactor: 'src',
283
+ blendAlphaOperation: 'add',
284
+ blendAlphaSrcFactor: 'one',
285
+ blendAlphaDstFactor: 'one-minus-src-alpha'
286
+ });
287
+ }
288
+
289
+ return parameters;
290
+ }
291
+
292
+ /** Draws one Tangram mesh into a host-provided luma.gl RenderPass. */
293
+ drawMesh({ mesh, program, renderPass, renderState, visibleTime }: TangramMeshDrawOptions) {
294
+ this.assertAlive();
295
+ if (!renderPass || !program || !program.vertex_shader_resource ||
296
+ !program.fragment_shader_resource) {
297
+ throw new Error('Tangram luma renderer requires an active render pass and shader resources');
298
+ }
299
+
300
+ const descriptor = mesh.getDrawDescriptor();
301
+ const pipeline = this.getPipeline(program, mesh.vertex_layout, descriptor, renderState, renderPass);
302
+
303
+ if (mesh.uniforms) {
304
+ program.saveUniforms(mesh.uniforms);
305
+ program.setUniforms(mesh.uniforms, false);
306
+ }
307
+
308
+ try {
309
+ program.uniform('1f', 'u_visible_time', visibleTime);
310
+ const wrapperBindings = program.getBindings();
311
+ this.snapshotMeshUniformBindings(mesh, program, wrapperBindings);
312
+ const bindings: Bindings = {};
313
+ for (const [name, resource] of Object.entries(wrapperBindings)) {
314
+ const native = this.bindingResources.get(resource);
315
+ if (native) bindings[name] = native;
316
+ else if (isNativeBinding(resource, this.device)) bindings[name] = resource;
317
+ else throw new Error(`Tangram binding '${name}' requires a native luma resource`);
318
+ }
319
+ assertPipelineBindings(pipeline, bindings);
320
+ const vertex_array = this.getVertexArray(mesh, pipeline, descriptor);
321
+ renderPass.setPipeline(pipeline);
322
+ renderPass.setBindings(bindings);
323
+ renderPass.setVertexArray(vertex_array);
324
+ const uniforms = program.getUniformValues();
325
+ const draw_succeeded = renderPass.draw({
326
+ vertexCount: descriptor.indexBuffer ? undefined : descriptor.vertexCount,
327
+ indexCount: descriptor.indexBuffer ? descriptor.indexCount : undefined,
328
+ uniforms: this.device.type === 'webgl' ? uniforms : undefined
329
+ });
330
+ return draw_succeeded === false && pipeline.isPending === true;
331
+ }
332
+ finally {
333
+ if (mesh.uniforms) {
334
+ program.restoreUniforms(mesh.uniforms);
335
+ }
336
+ }
337
+ }
338
+
339
+ /** Destroys cached pipelines, vertex arrays and uniform snapshots, not the host device. */
340
+ destroy() {
341
+ if (this.destroyed) return;
342
+ this.destroyed = true;
343
+ for (const resources of this.mesh_resources) this.disposeMeshResources(resources);
344
+ for (const resources of this.program_resources) this.disposeProgramResources(resources);
345
+ this.pipeline_cache = new WeakMap();
346
+ this.mesh_resource_cache = new WeakMap();
347
+ }
348
+
349
+ /** Copies mutable uniform blocks into storage unique to the encoded mesh draw. */
350
+ snapshotMeshUniformBindings(mesh: TangramDrawableMesh, program: TangramDrawableProgram, bindings: Record<string, object>) {
351
+ const uniform_blocks = program.uniform_blocks || {};
352
+ const snapshot_blocks = Object.entries(uniform_blocks)
353
+ .filter((entry): entry is [string, TangramDrawableUniformBlock & {data: ArrayBuffer}] =>
354
+ entry[1].snapshot_per_mesh && Boolean(entry[1].data)
355
+ );
356
+ if (snapshot_blocks.length === 0) {
357
+ return;
358
+ }
359
+
360
+ const mesh_buffers = this.getMeshResources(mesh).uniformBuffers;
361
+ for (const [name, uniform_buffer] of snapshot_blocks) {
362
+ let snapshot = mesh_buffers.get(name);
363
+ if (!snapshot || snapshot.byteLength !== uniform_buffer.byteLength) {
364
+ const buffer = this.device.createBuffer({
365
+ id: `tangram-mesh-${mesh.id}-${name}-uniforms`,
366
+ byteLength: uniform_buffer.byteLength,
367
+ usage: Buffer.UNIFORM | Buffer.COPY_DST
368
+ });
369
+ this.bindingResources.set(buffer, buffer);
370
+ snapshot?.buffer.destroy();
371
+ snapshot = {buffer, byteLength: uniform_buffer.byteLength};
372
+ mesh_buffers.set(name, snapshot);
373
+ }
374
+ snapshot.buffer.write(new Uint8Array(uniform_buffer.data));
375
+ bindings[name] = snapshot.buffer;
376
+ }
377
+ }
378
+
379
+ /** Cache pipelines by geometry, render state and the active pass's attachment formats. */
380
+ getPipeline(program: TangramDrawableProgram, vertex_layout: object, descriptor: TangramMeshDrawDescriptor,
381
+ render_state?: RenderPipelineParameters, renderPass?: RenderPass) {
382
+ this.assertAlive();
383
+ const resources = this.getProgramResources(program);
384
+ const layouts = resources.layouts;
385
+ let topologies = layouts.get(vertex_layout);
386
+ if (!topologies) {
387
+ topologies = new Map();
388
+ layouts.set(vertex_layout, topologies);
389
+ }
390
+ let states = topologies.get(descriptor.topology);
391
+ if (!states) {
392
+ states = new Map();
393
+ topologies.set(descriptor.topology, states);
394
+ }
395
+ // Implicit canvas passes also need an explicit depth format. In WebGPU,
396
+ // depthWrite=false does not otherwise create a depth-stencil pipeline,
397
+ // even when a non-writing annotation requests depth comparison.
398
+ const framebuffer = renderPass?.props?.framebuffer ??
399
+ (this.device.type === 'webgpu' && renderPass ? this.device.getDefaultCanvasContext().getCurrentFramebuffer() : undefined);
400
+ const attachments: Pick<RenderPipelineProps, 'colorAttachmentFormats' | 'depthStencilAttachmentFormat'> = {};
401
+ let parameters = render_state;
402
+ if (this.device.type === 'webgpu' && framebuffer) {
403
+ parameters = {...render_state,
404
+ sampleCount: framebuffer.colorAttachments[0]?.texture.samples ?? framebuffer.depthStencilAttachment?.texture.samples ?? 1,
405
+ ...(!framebuffer.depthStencilAttachment ? {depthWriteEnabled: false, depthCompare: 'always' as const} : {})};
406
+ attachments.colorAttachmentFormats = framebuffer.colorAttachments.map(attachment => {
407
+ const format = attachment.texture.format;
408
+ if (isDepthStencilFormat(format)) throw new Error('Color attachment requires a color texture');
409
+ return format;
410
+ });
411
+ const depthFormat = framebuffer.depthStencilAttachment?.texture.format;
412
+ if (depthFormat !== undefined) {
413
+ if (!isDepthStencilFormat(depthFormat)) throw new Error('Depth attachment requires a depth/stencil texture');
414
+ attachments.depthStencilAttachmentFormat = depthFormat;
415
+ }
416
+ }
417
+ const state_key = JSON.stringify({state: parameters || {}, attachments});
418
+ let pipeline = states.get(state_key);
419
+ if (!pipeline) {
420
+ const pipeline_options: RenderPipelineProps = {
421
+ id: `tangram-${program.name || program.id}-${descriptor.topology}-${states.size}`,
422
+ vs: program.vertex_shader_resource ?? undefined,
423
+ fs: program.fragment_shader_resource ?? undefined,
424
+ bufferLayout: [descriptor.bufferLayout],
425
+ topology: descriptor.topology,
426
+ disableWarnings: true,
427
+ ...attachments
428
+ };
429
+ if (parameters) {
430
+ pipeline_options.parameters = parameters;
431
+ }
432
+ pipeline = this.device.createRenderPipeline(pipeline_options);
433
+ states.set(state_key, pipeline);
434
+ resources.pipelines.add(pipeline);
435
+ }
436
+ return pipeline;
437
+ }
438
+
439
+ getVertexArray(mesh: TangramDrawableMesh, pipeline: RenderPipeline, descriptor: TangramMeshDrawDescriptor) {
440
+ this.assertAlive();
441
+ const pipelines_for_mesh = this.getMeshResources(mesh).vertexArrays;
442
+ let vertex_array = pipelines_for_mesh.get(pipeline);
443
+ if (vertex_array) {
444
+ return vertex_array;
445
+ }
446
+
447
+ vertex_array = this.device.createVertexArray({
448
+ id: `tangram-mesh-${mesh.id}-${pipeline.id}`,
449
+ shaderLayout: pipeline.shaderLayout,
450
+ bufferLayout: pipeline.bufferLayout
451
+ });
452
+ try {
453
+ const attributes = new Map(
454
+ pipeline.shaderLayout.attributes.map(attribute => [attribute.name, attribute])
455
+ );
456
+ for (const attribute of descriptor.bufferLayout.attributes) {
457
+ const shader_attribute = attributes.get(attribute.attribute);
458
+ if (shader_attribute) {
459
+ vertex_array.setBuffer(shader_attribute.location, descriptor.vertexBuffer!); // Portable meshes supply device buffers; classic descriptors may not.
460
+ }
461
+ }
462
+ for (const attribute of descriptor.staticAttributes) {
463
+ const shader_attribute = attributes.get(attribute.attribute);
464
+ if (shader_attribute) {
465
+ if (this.device.type === 'webgpu') {
466
+ throw new Error(
467
+ `Tangram WebGPU renderer requires '${attribute.attribute}' in a vertex buffer`
468
+ );
469
+ }
470
+ vertex_array.setConstantWebGL(
471
+ shader_attribute.location,
472
+ new Float32Array(attribute.value)
473
+ );
474
+ }
475
+ }
476
+ if (descriptor.indexBuffer) {
477
+ vertex_array.setIndexBuffer(descriptor.indexBuffer);
478
+ }
479
+ }
480
+ catch (error) {
481
+ vertex_array.destroy();
482
+ throw error;
483
+ }
484
+
485
+ pipelines_for_mesh.set(pipeline, vertex_array);
486
+ return vertex_array;
487
+ }
488
+
489
+ /** Create one cleanup generation per program without retaining the owner in a callback. */
490
+ private getProgramResources(program: TangramDrawableProgram): ProgramResources {
491
+ let resources = this.pipeline_cache.get(program);
492
+ if (!resources || resources.disposed) {
493
+ resources = {layouts: new WeakMap(), pipelines: new Set(), disposed: false, unsubscribe: () => {}};
494
+ const owned = resources;
495
+ resources.unsubscribe = observeGPUResourceDisposal(program, () => this.disposeProgramResources(owned));
496
+ this.pipeline_cache.set(program, resources);
497
+ this.program_resources.add(resources);
498
+ }
499
+ return resources;
500
+ }
501
+
502
+ /** Reuse live mesh storage, and observe only the resources belonging to that mesh. */
503
+ private getMeshResources(mesh: TangramDrawableMesh): MeshResources {
504
+ this.assertAlive();
505
+ let resources = this.mesh_resource_cache.get(mesh);
506
+ if (!resources || resources.disposed) {
507
+ resources = {vertexArrays: new Map(), uniformBuffers: new Map(), disposed: false, unsubscribe: () => {}};
508
+ const owned = resources;
509
+ resources.unsubscribe = observeGPUResourceDisposal(mesh, () => this.disposeMeshResources(owned));
510
+ this.mesh_resource_cache.set(mesh, resources);
511
+ this.mesh_resources.add(resources);
512
+ }
513
+ return resources;
514
+ }
515
+
516
+ /** Release a mesh's cached draw resources, leaving shared program pipelines alive. */
517
+ private disposeMeshResources(resources: MeshResources): void {
518
+ if (resources.disposed) return;
519
+ resources.disposed = true;
520
+ resources.unsubscribe();
521
+ this.mesh_resources.delete(resources);
522
+ for (const vertexArray of resources.vertexArrays.values()) vertexArray.destroy();
523
+ for (const snapshot of resources.uniformBuffers.values()) snapshot.buffer.destroy();
524
+ resources.vertexArrays.clear();
525
+ resources.uniformBuffers.clear();
526
+ }
527
+
528
+ /** Invalidate dependent vertex arrays before retiring one program generation's pipelines. */
529
+ private disposeProgramResources(resources: ProgramResources): void {
530
+ if (resources.disposed) return;
531
+ resources.disposed = true;
532
+ resources.unsubscribe();
533
+ this.program_resources.delete(resources);
534
+ for (const mesh of this.mesh_resources) {
535
+ for (const [pipeline, vertexArray] of mesh.vertexArrays) {
536
+ if (resources.pipelines.has(pipeline)) {
537
+ vertexArray.destroy();
538
+ mesh.vertexArrays.delete(pipeline);
539
+ }
540
+ }
541
+ }
542
+ for (const pipeline of resources.pipelines) pipeline.destroy();
543
+ resources.pipelines.clear();
544
+ resources.layouts = new WeakMap();
545
+ }
546
+
547
+ /** Prevent draw-time reuse after final backend teardown. */
548
+ private assertAlive(): void {
549
+ if (this.destroyed) throw new Error('Tangram GPU backend has been destroyed');
550
+ }
551
+ }
552
+
553
+ function validateDevice(device: Device) {
554
+ if (!device || !device.info ||
555
+ typeof device.createBuffer !== 'function' ||
556
+ typeof device.createShader !== 'function' ||
557
+ typeof device.createTexture !== 'function' ||
558
+ typeof device.createRenderPipeline !== 'function' ||
559
+ typeof device.createVertexArray !== 'function') {
560
+ throw new Error('Tangram requires a luma.gl Device');
561
+ }
562
+ }
563
+
564
+ function assertPipelineBindings(pipeline: RenderPipeline, bindings: Bindings) {
565
+ for (const binding of pipeline.shaderLayout.bindings) {
566
+ if (binding.type === 'sampler' && binding.name.endsWith('Sampler')) {
567
+ const texture_name = binding.name.slice(0, -'Sampler'.length);
568
+ if (bindings[texture_name]) {
569
+ continue;
570
+ }
571
+ }
572
+ if (binding.type !== 'uniform' && binding.type !== 'texture' && binding.type !== 'sampler') {
573
+ throw new Error(`Tangram luma renderer does not support '${binding.type}' bindings`);
574
+ }
575
+ if (!bindings[binding.name]) {
576
+ throw new Error(`Tangram luma renderer is missing '${binding.name}' binding`);
577
+ }
578
+ }
579
+ }
@@ -0,0 +1,51 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Buffer} from '@luma.gl/core';
6
+ import type {CommandEncoder, Device, Texture} from '@luma.gl/core';
7
+
8
+ /** A mip-zero rectangle in the texture's native texel coordinate system. */
9
+ export type PixelReadRectangle = {x: number; y: number; width: number; height: number};
10
+
11
+ /**
12
+ * Read tightly packed RGBA8 bytes through public luma core commands.
13
+ * WebGPU row padding is removed; row orientation stays native to the backend.
14
+ * The caller owns the COPY_SRC-capable texture/device. This call owns and releases its
15
+ * staging buffer, including when encoding, submission or mapping fails.
16
+ * WebGL's readAsync implementation may still synchronize with the GPU.
17
+ */
18
+ export async function readDevicePixels(device: Device, texture: Texture, rectangle: PixelReadRectangle): Promise<Uint8Array> {
19
+ const {x, y, width, height} = rectangle;
20
+ if (texture.format !== 'rgba8unorm' || texture.dimension !== '2d' || texture.samples !== 1) {
21
+ throw new Error('Pixel readback requires a single-sample rgba8unorm 2D texture');
22
+ }
23
+ if (![x, y, width, height].every(Number.isSafeInteger) || x < 0 || y < 0 || width <= 0 || height <= 0 ||
24
+ x + width > texture.width || y + height > texture.height) {
25
+ throw new Error('Pixel readback rectangle must be positive and inside the texture');
26
+ }
27
+ const rowBytes = width * 4;
28
+ const bytesPerRow = device.type === 'webgpu' ? Math.ceil(rowBytes / 256) * 256 : rowBytes;
29
+ const staging = device.createBuffer({id: 'tangram-pixel-readback',
30
+ byteLength: bytesPerRow * height, usage: Buffer.COPY_DST | Buffer.MAP_READ});
31
+ let encoder: CommandEncoder | undefined;
32
+ try {
33
+ encoder = device.createCommandEncoder({id: 'tangram-pixel-readback'});
34
+ encoder.copyTextureToBuffer({sourceTexture: texture, destinationBuffer: staging,
35
+ origin: [x, y, 0], width, height,
36
+ ...(device.type === 'webgpu' ? {bytesPerRow, rowsPerImage: height} : {})});
37
+ const commands = encoder.finish(); // finish releases the encoder; submit releases the command buffer.
38
+ encoder = undefined;
39
+ device.submit(commands);
40
+ const padded = await staging.readAsync();
41
+ if (padded.byteLength < bytesPerRow * height) throw new Error('Pixel readback returned incomplete data');
42
+ const pixels = new Uint8Array(rowBytes * height);
43
+ for (let row = 0; row < height; row++) {
44
+ pixels.set(padded.subarray(row * bytesPerRow, row * bytesPerRow + rowBytes), row * rowBytes);
45
+ }
46
+ return pixels;
47
+ } finally {
48
+ encoder?.destroy();
49
+ staging.destroy();
50
+ }
51
+ }
@@ -0,0 +1,32 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** One-shot observers; weak keys do not keep retired scene owners alive. */
6
+ const observers = new WeakMap<object, Set<() => void>>();
7
+
8
+ /** Register cache cleanup for a mesh retirement or shader-resource replacement. */
9
+ export function observeGPUResourceDisposal(owner: object, dispose: () => void): () => void {
10
+ let callbacks = observers.get(owner);
11
+ if (!callbacks) {
12
+ callbacks = new Set();
13
+ observers.set(owner, callbacks);
14
+ }
15
+ callbacks.add(dispose);
16
+ const registered = callbacks;
17
+ return () => { registered.delete(dispose); };
18
+ }
19
+
20
+ /** Drain a generation's observers, even if one fails or registers a new generation. */
21
+ export function notifyGPUResourceDisposal(owner: object): void {
22
+ const callbacks = observers.get(owner);
23
+ if (!callbacks) return;
24
+ observers.delete(owner);
25
+ const pending = [...callbacks];
26
+ callbacks.clear();
27
+ const errors: unknown[] = [];
28
+ for (const dispose of pending) {
29
+ try { dispose(); } catch (error) { errors.push(error); }
30
+ }
31
+ if (errors.length) throw new AggregateError(errors, 'GPU cache disposal failed');
32
+ }