@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,1310 @@
1
+ // Tangram
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) 2013-2016 Brett Camper and Mapzen
4
+ // Copyright (c) 2026 vis.gl contributors
5
+
6
+ // GL program wrapper to cache uniform locations/values, do compile-time pre-processing
7
+ // (injecting #defines and #pragma blocks into shaders), etc.
8
+
9
+ import log from '../utils/log';
10
+ import GLSL from './glsl';
11
+ import Texture from './texture';
12
+ import getExtension from './extensions';
13
+ import hashString from '../utils/hash';
14
+ import {notifyGPUResourceDisposal} from '../gpu/resource_lifecycle';
15
+ import type {MeshProgram} from './mesh-types';
16
+ import type {Shader, UniformValue} from '@luma.gl/core';
17
+ import type UniformBuffer from './uniform_buffer';
18
+ import type {TangramShaderOptions, TangramShaderProgramOptions} from '../gpu/tangram_gpu_backend';
19
+
20
+ import parseShaderErrors from 'gl-shader-errors';
21
+
22
+ // Regex patterns
23
+ const re_pragma = /^\s*#pragma.*$/gm; // for removing unused pragmas after shader block injection
24
+ const re_continue_line = /\\\s*\n/mg; // for removing backslash line continuations
25
+ const re_fragment_color = /\bgl_FragColor\b/g;
26
+ const re_texture_2d = /\btexture2D\b/g;
27
+ const re_texture_cube = /\btextureCube\b/g;
28
+ /** Wrapper ownership of cached raw programs; device-owned shaders use their own lifecycle. */
29
+ const programReferences = new WeakMap<WebGLProgram, number>();
30
+
31
+ /** Stage identity shared by source composition and compiler diagnostics. */
32
+ type ShaderStage = 'vertex' | 'fragment';
33
+ /** Classic context identity is part of the exact-source cache key. */
34
+ type ShaderContext = WebGLRenderingContext & {_tangram_id?: number};
35
+ /** Authored source fragments, either one string or an ordered list. */
36
+ type ShaderBlocks = Record<string, string | string[] | null>;
37
+ /** One injected source fragment with provenance for compiler diagnostics. */
38
+ type ShaderBlock = {key: string; source: string | null; num: number; scope?: string};
39
+ /** Source location within an injected Tangram block. */
40
+ type ShaderBlockLocation = {scope: string; name: string; num: string; line?: number; source?: string};
41
+ /** Compiler diagnostics retain both the original line and legacy block mapping. */
42
+ type ShaderDiagnostic = {message: string; file: number; line: number | ShaderBlockLocation | null | undefined; type?: ShaderStage; block?: ShaderBlockLocation | null};
43
+ /** Cached scalar/vector uniform and its optional legacy location. */
44
+ type CachedUniform = {name: string; method: string; value: UniformValue; saved_value?: UniformValue; location?: WebGLUniformLocation | null};
45
+ /** A sampler needs only classic binding or portable resource access, not every texture-wrapper field. */
46
+ type SamplerTexture = Partial<Pick<Texture, 'bind' | 'getResource'>>;
47
+ /** Program composition and portable resource allocation options. */
48
+ interface ShaderProgramOptions {
49
+ name?: string;
50
+ defines?: Record<string, unknown>;
51
+ blocks?: ShaderBlocks;
52
+ block_scopes?: Record<string, (string | null)[] | null>;
53
+ extensions?: string[];
54
+ uniforms?: Parameters<typeof GLSL.parseUniforms>[0];
55
+ uniform_blocks?: Record<string, UniformBuffer>;
56
+ deferUniformBlocks?: boolean;
57
+ deferTextureBindings?: boolean;
58
+ deferUniformUpdates?: boolean;
59
+ shaderLanguage?: 'glsl' | 'wgsl';
60
+ shaderFactory?: (options: TangramShaderOptions) => Shader;
61
+ shaderProgramValidator?: (options: TangramShaderProgramOptions) => void;
62
+ deviceShaderCompilation?: boolean;
63
+ glsl_version?: number;
64
+ }
65
+
66
+ export default class ShaderProgram {
67
+ /** Caller context is opaque in the portable path. */
68
+ declare gl: object | null;
69
+ /** Stable wrapper identity, independent of shared compiled resources. */
70
+ declare id: number;
71
+ declare name: string | undefined;
72
+ declare vertex_source: string;
73
+ declare computed_vertex_source: string | null | undefined;
74
+ declare computed_fragment_source: string | null | undefined;
75
+ declare compiled: boolean;
76
+ declare compiling: boolean;
77
+ declare error: unknown;
78
+ declare defines: Record<string, unknown>;
79
+ declare blocks: ShaderBlocks;
80
+ declare block_scopes: Record<string, (string | null)[] | null>;
81
+ declare extensions: string[];
82
+ declare dependent_uniforms: ShaderProgramOptions['uniforms'];
83
+ declare uniforms: Record<string, CachedUniform>;
84
+ declare uniform_blocks: Record<string, UniformBuffer>;
85
+ declare defer_uniform_blocks: boolean;
86
+ declare defer_texture_bindings: boolean;
87
+ declare defer_uniform_updates: boolean;
88
+ declare texture_uniforms: Record<string, SamplerTexture>;
89
+ declare shader_language: 'glsl' | 'wgsl';
90
+ declare shader_factory: ShaderProgramOptions['shaderFactory'];
91
+ declare shader_program_validator: ShaderProgramOptions['shaderProgramValidator'];
92
+ declare device_shader_compilation: boolean;
93
+ declare vertex_shader_resource: Shader | null | undefined;
94
+ declare fragment_shader_resource: Shader | null | undefined;
95
+ declare glsl_version: number;
96
+ declare attribs: Record<string, {name: string; location: number}>;
97
+ declare shader_errors: ShaderDiagnostic[];
98
+ declare texture_unit: number;
99
+ declare saved_texture_unit: number;
100
+ declare saved_texture_uniforms: Record<string, SamplerTexture>;
101
+ declare saved_uniform_block_data: Map<UniformBuffer, Uint8Array> | null;
102
+ declare static id: number;
103
+ declare static blocks: ShaderBlocks;
104
+ declare static shaders_by_source: Record<string, WebGLShader>;
105
+ declare static resetCurrent: () => void;
106
+ declare static releaseProgram: (context: ShaderContext, program: WebGLProgram | null) => void;
107
+ declare static convertToWebGL2: (source: string, stage: ShaderStage) => string;
108
+ declare static buildDefineString: (defines: Record<string, unknown>) => string;
109
+ declare static buildExtensionString: (extensions?: string[]) => string;
110
+ declare static updateProgram: (context: ShaderContext, program: WebGLProgram | null, vertex: string, fragment: string, resources?: {vertex_shader?: Shader; fragment_shader?: Shader}) => WebGLProgram;
111
+ declare static createShader: (context: ShaderContext, source: string, stage: number) => WebGLShader;
112
+ /** Program used when a classic mesh draw omits an explicit program. */
113
+ declare static current: MeshProgram | null;
114
+ /** Linked raw program owned by this wrapper, or null for device compilation. */
115
+ declare program: WebGLProgram | null;
116
+ /** Authored fragment source, before Tangram shader block composition. */
117
+ declare fragment_source: string;
118
+ /** Raw programs shared by exact source and context identity. */
119
+ declare static programs_by_source: Record<string, WebGLProgram>;
120
+ /** Clear source lookup caches without changing ownership of live programs. */
121
+ declare static reset: () => void;
122
+ /** Global shader defines shared by the Tangram block composer. */
123
+ declare static defines: Record<string, unknown>;
124
+ /** Append named GLSL blocks; implemented below for legacy compatibility. */
125
+ declare static addBlock: (key: string, ...blocks: string[]) => void;
126
+ /** Remove a named GLSL block; implemented below for legacy compatibility. */
127
+ declare static removeBlock: (key: string) => void;
128
+ /** Replaces a named Tangram shader block with the supplied source fragments. */
129
+ static declare replaceBlock: (key: string, ...blocks: string[]) => void;
130
+
131
+ constructor(gl: object | null, vertex_source: string, fragment_source: string, options: ShaderProgramOptions = {}) {
132
+ options = options || {};
133
+
134
+ this.gl = gl;
135
+ this.program = null;
136
+ this.compiled = false;
137
+ this.compiling = false;
138
+ this.error = null;
139
+
140
+ // key/values inserted as #defines into shaders at compile-time
141
+ this.defines = Object.assign({}, options.defines||{});
142
+
143
+ // key/values for blocks that can be injected into shaders at compile-time
144
+ this.blocks = Object.assign({}, options.blocks||{});
145
+ this.block_scopes = Object.assign({}, options.block_scopes||{});
146
+
147
+ // list of extensions to activate
148
+ this.extensions = options.extensions || [];
149
+
150
+ // JS-object uniforms that are expected by this program, their types are inferred and definitions
151
+ // for each will be injected.
152
+ this.dependent_uniforms = options.uniforms;
153
+
154
+ this.uniforms = {}; // program locations of uniforms, lazily added as each uniform is set
155
+ this.uniform_blocks = Object.assign({}, options.uniform_blocks || {});
156
+ this.defer_uniform_blocks = options.deferUniformBlocks === true;
157
+ this.defer_texture_bindings = options.deferTextureBindings === true;
158
+ this.defer_uniform_updates = options.deferUniformUpdates === true;
159
+ this.texture_uniforms = {};
160
+ this.shader_language = options.shaderLanguage || 'glsl';
161
+ this.shader_factory = options.shaderFactory;
162
+ this.shader_program_validator = options.shaderProgramValidator;
163
+ this.device_shader_compilation = options.deviceShaderCompilation === true;
164
+ this.vertex_shader_resource = null;
165
+ this.fragment_shader_resource = null;
166
+ this.glsl_version = options.glsl_version || (Object.keys(this.uniform_blocks).length > 0 ? 300 : 100);
167
+ this.attribs = {}; // program locations of vertex attributes, lazily added as each attribute is accessed
168
+
169
+ this.vertex_source = vertex_source;
170
+ this.fragment_source = fragment_source;
171
+
172
+ this.id = ShaderProgram.id++;
173
+ this.name = options.name; // can provide a program name (useful for debugging)
174
+ }
175
+
176
+ /** Only legacy compilation/binding may inspect raw WebGL capabilities. */
177
+ private get classicContext(): ShaderContext {
178
+ if (isShaderContext(this.gl)) return this.gl;
179
+ throw new Error('Classic shaders require a WebGL context');
180
+ }
181
+
182
+ destroy() {
183
+ if (this.shader_language === 'glsl' && !this.device_shader_compilation) {
184
+ this.classicContext.useProgram(null);
185
+ ShaderProgram.current = null;
186
+ ShaderProgram.releaseProgram(this.classicContext, this.program);
187
+ }
188
+ this.destroyShaderResources();
189
+ this.program = null;
190
+ this.uniforms = {};
191
+ this.texture_uniforms = {};
192
+ this.attribs = {};
193
+ this.compiled = false;
194
+ }
195
+
196
+ // Use program wrapper with simple state cache
197
+ use({ bindUniformBlocks = !this.defer_uniform_blocks } = {}) {
198
+ if (!this.compiled) {
199
+ return;
200
+ }
201
+
202
+ const changed = ShaderProgram.current !== this;
203
+ if (changed && this.shader_language === 'glsl' && !this.defer_uniform_updates) {
204
+ this.classicContext.useProgram(this.program);
205
+ }
206
+ ShaderProgram.current = this;
207
+ if (bindUniformBlocks) {
208
+ this.bindUniformBlocks({ force: true });
209
+ }
210
+ }
211
+
212
+ compile() {
213
+ if (this.shader_language !== 'glsl') {
214
+ return this.compilePortable();
215
+ }
216
+ if (this.device_shader_compilation &&
217
+ (!this.shader_factory || !this.shader_program_validator)) {
218
+ throw new Error(
219
+ 'ShaderProgram: device compilation requires shaderFactory and shaderProgramValidator'
220
+ );
221
+ }
222
+ if (this.compiling) {
223
+ throw(new Error(`ShaderProgram.compile(): skipping for ${this.id} (${this.name}) because already compiling`));
224
+ }
225
+ this.compiling = true;
226
+ this.compiled = false;
227
+ this.error = null;
228
+
229
+ // Copy sources from pre-modified template
230
+ this.computed_vertex_source = this.vertex_source;
231
+ this.computed_fragment_source = this.fragment_source;
232
+
233
+ // Check for extension availability
234
+ let extensions = this.checkExtensions();
235
+
236
+ // Make list of defines to be injected later
237
+ var defines = this.buildDefineList();
238
+
239
+ // Inject user-defined blocks (arbitrary code points matching named #pragmas)
240
+ // Replace according to this pattern:
241
+ // #pragma tangram: [key]
242
+ // e.g. #pragma tangram: global
243
+
244
+ // Gather all block code snippets
245
+ var blocks = this.buildShaderBlockList();
246
+ var regexp;
247
+
248
+ for (var key in blocks) {
249
+ var block = blocks[key];
250
+ if (!block || (Array.isArray(block) && block.length === 0)) {
251
+ continue;
252
+ }
253
+
254
+ // First find code replace points in shaders
255
+ regexp = new RegExp('^\\s*#pragma\\s+tangram:\\s+' + key + '\\s*$', 'm');
256
+ var inject_vertex = this.computed_vertex_source.match(regexp);
257
+ var inject_fragment = this.computed_fragment_source.match(regexp);
258
+
259
+ // Avoid network request if nothing to replace
260
+ if (inject_vertex == null && inject_fragment == null) {
261
+ continue;
262
+ }
263
+
264
+ // Combine all blocks into one string
265
+ var source = '';
266
+ block.forEach(val => {
267
+ // Mark start and end of each block with metadata (which can be extracted from
268
+ // final source for error handling, debugging, etc.)
269
+ let mark = `${val.scope}, ${val.key}, ${val.num}`;
270
+ source += `\n// tangram-block-start: ${mark}\n`;
271
+ source += val.source;
272
+ source += `\n// tangram-block-end: ${mark}\n`;
273
+ });
274
+
275
+ // Inject
276
+ if (inject_vertex != null) {
277
+ this.computed_vertex_source = this.computed_vertex_source.replace(regexp, source);
278
+ }
279
+ if (inject_fragment != null) {
280
+ this.computed_fragment_source = this.computed_fragment_source.replace(regexp, source);
281
+ }
282
+
283
+ // Add a #define for this injection point
284
+ defines['TANGRAM_BLOCK_' + key.replace(/[\s-]+/g, '_').toUpperCase()] = true;
285
+ }
286
+
287
+ // Clean-up any #pragmas that weren't replaced (to prevent compiler warnings)
288
+ this.computed_vertex_source = this.computed_vertex_source.replace(re_pragma, '');
289
+ this.computed_fragment_source = this.computed_fragment_source.replace(re_pragma, '');
290
+
291
+ // Inject uniform definitions
292
+ this.ensureUniforms(this.dependent_uniforms);
293
+ this.ensureUniformBlocks();
294
+
295
+ // Build & inject extensions & defines
296
+ // This is done *after* code injection so that we can add defines for which code points were injected
297
+ let precision = '';
298
+ // Device compilation targets WebGL 2, where highp float is required.
299
+ // Only the standalone legacy path needs a raw WebGL 1 precision query.
300
+ let high = this.device_shader_compilation ? {precision: 1} :
301
+ this.classicContext.getShaderPrecisionFormat(this.classicContext.FRAGMENT_SHADER, this.classicContext.HIGH_FLOAT);
302
+ if (high && high.precision > 0) {
303
+ precision = 'precision highp float;\n';
304
+ }
305
+ else {
306
+ precision = 'precision mediump float;\n';
307
+ }
308
+
309
+ defines['TANGRAM_VERTEX_SHADER'] = true;
310
+ defines['TANGRAM_FRAGMENT_SHADER'] = false;
311
+ defines['TANGRAM_WEBGL2'] = this.glsl_version >= 300;
312
+ this.computed_vertex_source =
313
+ precision +
314
+ ShaderProgram.buildDefineString(defines) +
315
+ this.computed_vertex_source;
316
+
317
+ // Precision qualifier only valid in fragment shader
318
+ // NB: '#extension' statements added to fragment shader only, as IE11 throws error when they appear in
319
+ // vertex shader (even when guarded by #ifdef), and no WebGL extensions require '#extension' in vertex shaders
320
+ defines['TANGRAM_VERTEX_SHADER'] = false;
321
+ defines['TANGRAM_FRAGMENT_SHADER'] = true;
322
+ this.computed_fragment_source =
323
+ ShaderProgram.buildExtensionString(extensions) +
324
+ precision +
325
+ ShaderProgram.buildDefineString(defines) +
326
+ this.computed_fragment_source;
327
+
328
+ // Replace multi-line backslashes
329
+ this.computed_vertex_source = this.computed_vertex_source.replace(re_continue_line, '');
330
+ this.computed_fragment_source = this.computed_fragment_source.replace(re_continue_line, '');
331
+ if (this.glsl_version >= 300) {
332
+ this.computed_vertex_source = ShaderProgram.convertToWebGL2(this.computed_vertex_source, 'vertex');
333
+ this.computed_fragment_source = ShaderProgram.convertToWebGL2(this.computed_fragment_source, 'fragment');
334
+ }
335
+
336
+ // Compile & set uniforms to cached values
337
+ try {
338
+ let shader_resources;
339
+ if (this.shader_factory) {
340
+ shader_resources = this.createShaderResources(
341
+ this.computed_vertex_source,
342
+ this.computed_fragment_source
343
+ );
344
+ }
345
+ if (this.device_shader_compilation) {
346
+ try {
347
+ if (!this.shader_program_validator || !shader_resources) throw new Error('ShaderProgram: missing device shader resources');
348
+ this.shader_program_validator({
349
+ id: this.name || String(this.id),
350
+ vertexShader: shader_resources.vertex_shader,
351
+ fragmentShader: shader_resources.fragment_shader
352
+ });
353
+ }
354
+ catch (error) {
355
+ destroyShaderResource(shader_resources && shader_resources.vertex_shader);
356
+ destroyShaderResource(shader_resources && shader_resources.fragment_shader);
357
+ throw error;
358
+ }
359
+ this.destroyShaderResources();
360
+ this.vertex_shader_resource = shader_resources && shader_resources.vertex_shader;
361
+ this.fragment_shader_resource = shader_resources && shader_resources.fragment_shader;
362
+ this.program = null;
363
+ }
364
+ else {
365
+ try {
366
+ this.program = ShaderProgram.updateProgram(
367
+ this.classicContext,
368
+ this.program,
369
+ this.computed_vertex_source,
370
+ this.computed_fragment_source,
371
+ shader_resources
372
+ );
373
+ }
374
+ catch (error) {
375
+ destroyShaderResource(shader_resources && shader_resources.vertex_shader);
376
+ destroyShaderResource(shader_resources && shader_resources.fragment_shader);
377
+ throw error;
378
+ }
379
+ this.destroyShaderResources();
380
+ this.vertex_shader_resource = shader_resources && shader_resources.vertex_shader;
381
+ this.fragment_shader_resource = shader_resources && shader_resources.fragment_shader;
382
+ }
383
+ this.compiled = true;
384
+ this.compiling = false;
385
+ ShaderProgram.current = null;
386
+ for (const uniform_buffer of Object.values(this.uniform_blocks)) {
387
+ if (typeof uniform_buffer.invalidateProgram === 'function') {
388
+ uniform_buffer.invalidateProgram(this.program);
389
+ }
390
+ }
391
+ }
392
+ catch(error) {
393
+ this.program = null;
394
+ this.compiled = false;
395
+ this.compiling = false;
396
+ this.error = error;
397
+
398
+ // shader error info
399
+ if (error instanceof Object) Object.assign(error, {vertex_shader_source: this.computed_vertex_source, fragment_shader_source: this.computed_fragment_source});
400
+
401
+ if (isShaderFailure(error)) {
402
+ this.shader_errors = error.errors;
403
+ this.shader_errors.forEach(e => {
404
+ e.type = error.type;
405
+ if (typeof e.line === 'number') {
406
+ e.block = this.block(error.type, e.line);
407
+ e.line = this.block(error.type, e.line);
408
+ }
409
+ });
410
+ Object.assign(error, {shader_errors: this.shader_errors});
411
+ }
412
+ throw error;
413
+ }
414
+
415
+ // Discard shader sources after successful compilation
416
+ this.computed_vertex_source = null;
417
+ this.computed_fragment_source = null;
418
+
419
+ this.use();
420
+ this.refreshUniforms();
421
+ this.refreshAttributes();
422
+ }
423
+
424
+ createShaderResources(vertex_source: string, fragment_source: string) {
425
+ if (!this.shader_factory) throw new Error('ShaderProgram: portable compilation requires a shaderFactory');
426
+ let vertex_shader: Shader | undefined;
427
+ try {
428
+ const vertex_options: TangramShaderOptions = {
429
+ id: `${this.name || this.id}-vertex`,
430
+ stage: 'vertex',
431
+ language: this.shader_language,
432
+ source: vertex_source
433
+ };
434
+ const fragment_options: TangramShaderOptions = {
435
+ id: `${this.name || this.id}-fragment`,
436
+ stage: 'fragment',
437
+ language: this.shader_language,
438
+ source: fragment_source
439
+ };
440
+ if (this.shader_language === 'wgsl') {
441
+ vertex_options.entryPoint = 'vertexMain';
442
+ fragment_options.entryPoint = 'fragmentMain';
443
+ }
444
+ vertex_shader = this.shader_factory(vertex_options);
445
+ const fragment_shader = this.shader_factory(fragment_options);
446
+ const resources_valid = vertex_shader && typeof vertex_shader.destroy === 'function' &&
447
+ fragment_shader && typeof fragment_shader.destroy === 'function';
448
+ const handles_valid = this.shader_language !== 'glsl' || this.device_shader_compilation ||
449
+ (vertex_shader.handle && fragment_shader.handle);
450
+ if (!resources_valid || !handles_valid) {
451
+ destroyShaderResource(fragment_shader);
452
+ throw new Error('ShaderProgram: shaderFactory must return a portable shader resource');
453
+ }
454
+ return { vertex_shader, fragment_shader };
455
+ }
456
+ catch (error) {
457
+ destroyShaderResource(vertex_shader);
458
+ throw error;
459
+ }
460
+ }
461
+
462
+ destroyShaderResources() {
463
+ try {
464
+ notifyGPUResourceDisposal(this);
465
+ }
466
+ finally {
467
+ destroyShaderResource(this.vertex_shader_resource);
468
+ destroyShaderResource(this.fragment_shader_resource);
469
+ this.vertex_shader_resource = null;
470
+ this.fragment_shader_resource = null;
471
+ }
472
+ }
473
+
474
+ // Compile a non-GLSL program without creating or linking a raw WebGL program.
475
+ compilePortable() {
476
+ if (this.shader_language !== 'wgsl') {
477
+ throw new Error(`ShaderProgram: unsupported shader language '${this.shader_language}'`);
478
+ }
479
+ if (!this.shader_factory) {
480
+ throw new Error('ShaderProgram: portable compilation requires a shaderFactory');
481
+ }
482
+ if (this.compiling) {
483
+ throw new Error(`ShaderProgram.compile(): ${this.id} (${this.name}) is already compiling`);
484
+ }
485
+
486
+ this.compiling = true;
487
+ this.compiled = false;
488
+ this.error = null;
489
+ this.computed_vertex_source = this.prependPortableUniformBlocks(this.vertex_source);
490
+ this.computed_fragment_source = this.prependPortableUniformBlocks(this.fragment_source);
491
+
492
+ try {
493
+ const shader_resources = this.createShaderResources(
494
+ this.computed_vertex_source,
495
+ this.computed_fragment_source
496
+ );
497
+ this.destroyShaderResources();
498
+ this.vertex_shader_resource = shader_resources.vertex_shader;
499
+ this.fragment_shader_resource = shader_resources.fragment_shader;
500
+ this.program = null;
501
+ this.compiled = true;
502
+ this.compiling = false;
503
+ ShaderProgram.current = null;
504
+ }
505
+ catch (error) {
506
+ this.program = null;
507
+ this.compiled = false;
508
+ this.compiling = false;
509
+ this.error = error;
510
+ if (error instanceof Object) Object.assign(error, {vertex_shader_source: this.computed_vertex_source, fragment_shader_source: this.computed_fragment_source});
511
+ throw error;
512
+ }
513
+
514
+ this.computed_vertex_source = null;
515
+ this.computed_fragment_source = null;
516
+ this.use();
517
+ }
518
+
519
+ prependPortableUniformBlocks(source: string) {
520
+ const declarations = Object.values(this.uniform_blocks)
521
+ .filter(uniform_buffer => typeof uniform_buffer.getDeclaration === 'function')
522
+ .map(uniform_buffer => uniform_buffer.getDeclaration({ language: this.shader_language }));
523
+ return declarations.length > 0 ? `${declarations.join('\n')}\n${source}` : source;
524
+ }
525
+
526
+ // Make list of defines (global, then program-specific)
527
+ buildDefineList() {
528
+ var d, defines: Record<string, unknown> = {};
529
+ for (d in ShaderProgram.defines) {
530
+ defines[d] = ShaderProgram.defines[d];
531
+ }
532
+ for (d in this.defines) {
533
+ defines[d] = this.defines[d];
534
+ }
535
+ return defines;
536
+ }
537
+
538
+ // Make list of shader blocks (global, then program-specific)
539
+ buildShaderBlockList() {
540
+ let key: string, blocks: Record<string, ShaderBlock[]> = {};
541
+
542
+ // Global blocks
543
+ for (key in ShaderProgram.blocks) {
544
+ blocks[key] = [];
545
+ const sources = ShaderProgram.blocks[key];
546
+ if (Array.isArray(sources)) {
547
+ blocks[key].push(
548
+ ...sources.map((source, num) => {
549
+ return { key, source, num, scope: 'ShaderProgram' };
550
+ })
551
+ );
552
+ }
553
+ else {
554
+ blocks[key] = [{ key, source: sources, num: 0, scope: 'ShaderProgram' }];
555
+ }
556
+ }
557
+
558
+ // Program-specific blocks
559
+ for (key in this.blocks) {
560
+ blocks[key] = blocks[key] || [];
561
+ const sources = this.blocks[key];
562
+ if (Array.isArray(sources)) {
563
+ let scopes = (this.block_scopes && this.block_scopes[key]) || [];
564
+ let cur_scope: string | null = null, num = 0;
565
+
566
+ for (let b=0; b < sources.length; b++) {
567
+ // Count blocks relative to current scope
568
+ if (scopes[b] !== cur_scope) {
569
+ cur_scope = scopes[b];
570
+ num = 0;
571
+ }
572
+
573
+ blocks[key].push({
574
+ key,
575
+ source: sources[b],
576
+ num,
577
+ scope: cur_scope || this.name
578
+ });
579
+
580
+ num++;
581
+ }
582
+ }
583
+ else {
584
+ // TODO: address discrepancy in array vs. single-value blocks
585
+ // styles assume array when tracking block scopes
586
+ blocks[key].push({ key, source: sources, num: 0, scope: this.name });
587
+ }
588
+ }
589
+ return blocks;
590
+ }
591
+
592
+ // Inject uniform definitions
593
+ ensureUniforms(uniforms: ShaderProgramOptions['uniforms']) {
594
+ if (!uniforms) {
595
+ return;
596
+ }
597
+
598
+ // Get GLSL definitions
599
+ const inject = Object.entries(uniforms).
600
+ map(([name, uniform]) => GLSL.defineUniform(name, uniform)).
601
+ filter(x => x);
602
+
603
+ // Inject uniforms
604
+ // NOTE: these are injected at the very top of the shaders, even before any #defines or #pragmas are added
605
+ // this could cause some issues with certain #pragmas, or other functions that might expect #defines
606
+ this.computed_vertex_source = inject.join('\n') + this.computed_vertex_source;
607
+ this.computed_fragment_source = inject.join('\n') + this.computed_fragment_source;
608
+ }
609
+
610
+ // Replace standalone uniforms with std140 uniform-block declarations.
611
+ ensureUniformBlocks() {
612
+ for (const uniform_buffer of Object.values(this.uniform_blocks)) {
613
+ const uniforms = uniform_buffer.layout && uniform_buffer.layout.uniforms;
614
+ if (!uniforms || typeof uniform_buffer.getDeclaration !== 'function') {
615
+ continue;
616
+ }
617
+
618
+ for (const name of Object.keys(uniforms)) {
619
+ const declaration = new RegExp(
620
+ `^\\s*uniform\\s+[A-Za-z0-9_]+\\s+${escapeRegExp(name)}\\s*;\\s*(?://.*)?$`,
621
+ 'gm'
622
+ );
623
+ this.computed_vertex_source = this.computed_vertex_source?.replace(declaration, '') ?? null;
624
+ this.computed_fragment_source = this.computed_fragment_source?.replace(declaration, '') ?? null;
625
+ }
626
+
627
+ const block_define = `TANGRAM_UNIFORM_BLOCK_${uniform_buffer.name.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()}`;
628
+ const declaration = `#define ${block_define}\n${uniform_buffer.getDeclaration()}\n`;
629
+ this.computed_vertex_source = declaration + this.computed_vertex_source;
630
+ this.computed_fragment_source = declaration + this.computed_fragment_source;
631
+ }
632
+ }
633
+
634
+ // Set uniforms from a JS object, with inferred types
635
+ setUniforms(uniforms: Record<string, unknown>, reset_texture_unit = true) {
636
+ if (!this.compiled) {
637
+ return;
638
+ }
639
+
640
+ // TODO: only update uniforms when changed
641
+
642
+ // Texture units must be tracked and incremented each time a texture sampler uniform is set.
643
+ // By default, the texture unit is reset to 0 each time setUniforms is called, but they can
644
+ // also be preserved, for example in cases where multiple calls to setUniforms are expected
645
+ // (e.g. program-specific uniforms followed by mesh-specific ones).
646
+ if (reset_texture_unit) {
647
+ this.texture_unit = 0;
648
+ }
649
+
650
+ // Parse uniform types and values from the JS object
651
+ GLSL.parseUniforms(uniforms)
652
+ .forEach(({ name, type, value, method }) => {
653
+ if (type === 'sampler2D' && typeof value === 'string') {
654
+ // For textures, we need to track texture units, so we have a special setter
655
+ this.setTextureUniform(name, value);
656
+ }
657
+ else if (isNumericUniform(value)) {
658
+ this.uniform(method, name, value);
659
+ }
660
+ });
661
+ }
662
+
663
+ // Register a WebGL2 uniform buffer with this program.
664
+ setUniformBlock(name: string, uniform_buffer: UniformBuffer) {
665
+ this.uniform_blocks[name] = uniform_buffer;
666
+ if (this.compiled && !this.defer_uniform_blocks) {
667
+ uniform_buffer.bind(this.program);
668
+ }
669
+ }
670
+
671
+ // Bind all registered WebGL2 uniform buffers to this program.
672
+ bindUniformBlocks({ force = false } = {}) {
673
+ if (!this.compiled || (this.defer_uniform_blocks && !force)) {
674
+ return;
675
+ }
676
+ for (const uniform_buffer of Object.values(this.uniform_blocks)) {
677
+ uniform_buffer.bind(this.program);
678
+ }
679
+ }
680
+
681
+ // Return portable luma.gl binding metadata for registered uniform blocks.
682
+ getUniformBlockBindingLayouts() {
683
+ return Object.values(this.uniform_blocks)
684
+ .filter(uniform_buffer => typeof uniform_buffer.getBindingLayout === 'function')
685
+ .map(uniform_buffer => uniform_buffer.getBindingLayout());
686
+ }
687
+
688
+ // Return luma.gl Buffer resources keyed by shader block name.
689
+ getUniformBlockBindings() {
690
+ const bindings: Record<string, import('./uniform_buffer').UniformBufferResource> = {};
691
+ for (const [name, uniform_buffer] of Object.entries(this.uniform_blocks)) {
692
+ if (uniform_buffer.buffer_resource) {
693
+ uniform_buffer.upload();
694
+ bindings[name] = uniform_buffer.buffer_resource;
695
+ }
696
+ }
697
+ return bindings;
698
+ }
699
+
700
+ // Return luma.gl Texture resources keyed by sampler uniform name.
701
+ getTextureBindings() {
702
+ const bindings: Record<string, import('./texture').TextureResource> = {};
703
+ for (const [uniform_name, texture] of Object.entries(this.texture_uniforms)) {
704
+ const resource = texture && texture.getResource && texture.getResource();
705
+ if (resource) {
706
+ if (this.shader_language === 'glsl') {
707
+ bindings[uniform_name] = resource;
708
+ }
709
+ // WebGL reflects a sampler array through its base uniform name,
710
+ // while Tangram assigns each texture through an indexed name.
711
+ // Preserve the first element as the base binding expected by luma.
712
+ if (uniform_name.endsWith('[0]')) {
713
+ bindings[uniform_name.slice(0, -3)] = resource;
714
+ }
715
+ else if (this.shader_language !== 'glsl' && !uniform_name.includes('[')) {
716
+ bindings[uniform_name] = resource;
717
+ }
718
+ }
719
+ }
720
+ return bindings;
721
+ }
722
+
723
+ // Return all portable shader resources for a renderer-owned draw call.
724
+ getBindings() {
725
+ return Object.assign({}, this.getUniformBlockBindings(), this.getTextureBindings());
726
+ }
727
+
728
+ // Return the current scalar uniform values for renderers that own the draw call.
729
+ getUniformValues() {
730
+ const values: Record<string, UniformValue> = {};
731
+ for (const [name, uniform] of Object.entries(this.uniforms)) {
732
+ if (uniform.value !== undefined) {
733
+ values[name] = uniform.value;
734
+ // A one-element uniform array is reflected through its base
735
+ // name in WebGL, even though Tangram assigns its first index.
736
+ if (name.endsWith('[0]')) {
737
+ values[name.slice(0, -3)] = uniform.value;
738
+ }
739
+ }
740
+ }
741
+ return values;
742
+ }
743
+
744
+ // Cache some or all uniform values so they can be restored
745
+ saveUniforms(subset?: Record<string, unknown>) {
746
+ let uniforms = subset || this.uniforms;
747
+ for (let u in uniforms) {
748
+ let uniform = this.uniforms[u];
749
+ if (uniform) {
750
+ uniform.saved_value = uniform.value;
751
+ }
752
+ }
753
+ this.saved_uniform_block_data = new Map();
754
+ for (const uniform_buffer of Object.values(this.uniform_blocks)) {
755
+ const block_uniforms = uniform_buffer.layout && uniform_buffer.layout.uniforms;
756
+ if (uniform_buffer.data && block_uniforms &&
757
+ Object.keys(uniforms).some(name => block_uniforms[name])) {
758
+ this.saved_uniform_block_data.set(
759
+ uniform_buffer,
760
+ new Uint8Array(uniform_buffer.data).slice()
761
+ );
762
+ }
763
+ }
764
+ this.saved_texture_unit = this.texture_unit || 0;
765
+ this.saved_texture_uniforms = Object.assign({}, this.texture_uniforms);
766
+ }
767
+
768
+ // Restore some or all uniforms to saved values
769
+ restoreUniforms(subset?: Record<string, unknown>) {
770
+ let uniforms = subset || this.uniforms;
771
+ for (let u in uniforms) {
772
+ let uniform = this.uniforms[u];
773
+ if (uniform && uniform.saved_value !== undefined) {
774
+ uniform.value = uniform.saved_value;
775
+ this.updateUniform(uniform);
776
+ }
777
+ }
778
+ for (const [uniform_buffer, data] of this.saved_uniform_block_data || []) {
779
+ if (!uniform_buffer.data) continue;
780
+ new Uint8Array(uniform_buffer.data).set(data);
781
+ uniform_buffer.dirty = true;
782
+ }
783
+ this.saved_uniform_block_data = null;
784
+ this.texture_unit = this.saved_texture_unit || 0;
785
+ this.texture_uniforms = this.saved_texture_uniforms || {};
786
+ }
787
+
788
+ // Set a texture uniform, finds texture by name or creates a new one
789
+ setTextureUniform(uniform_name: string, texture_name: string) {
790
+ var texture = Texture.textures[texture_name];
791
+ if (texture == null || !isSamplerTexture(texture)) {
792
+ log('warn', `Cannot find texture '${texture_name}'`);
793
+ return;
794
+ }
795
+
796
+ this.texture_uniforms[uniform_name] = texture;
797
+ if (!this.defer_texture_bindings) {
798
+ if (typeof texture.bind !== 'function') throw new Error('ShaderProgram: classic texture requires bind');
799
+ texture.bind(this.texture_unit);
800
+ this.uniform('1i', uniform_name, this.texture_unit);
801
+ }
802
+ this.texture_unit++; // TODO: track max texture units and log/throw errors
803
+ }
804
+
805
+ // ex: program.uniform('3fv', 'position', [x, y, z]);
806
+ // TODO: only update uniforms when changed
807
+ uniform(method: string, name: string, value: UniformValue) { // 'value' is a method-appropriate arguments list
808
+ if (!this.compiled) {
809
+ return;
810
+ }
811
+
812
+ for (const uniform_buffer of Object.values(this.uniform_blocks)) {
813
+ if (uniform_buffer.layout && uniform_buffer.layout.uniforms[name]) {
814
+ uniform_buffer.setUniform(name, value);
815
+ return;
816
+ }
817
+ }
818
+
819
+ this.uniforms[name] = this.uniforms[name] || {name, method, value};
820
+ let uniform = this.uniforms[name];
821
+ uniform.name = name;
822
+ if (uniform.location === undefined && !this.defer_uniform_updates) {
823
+ if (!this.program) return;
824
+ uniform.location = this.classicContext.getUniformLocation(this.program, name);
825
+ }
826
+ uniform.method = method;
827
+ uniform.value = value;
828
+ this.updateUniform(uniform);
829
+ }
830
+
831
+ // Set a single uniform
832
+ updateUniform(uniform: CachedUniform | undefined) {
833
+ if (!this.compiled) {
834
+ return;
835
+ }
836
+
837
+ if (!uniform) {
838
+ return;
839
+ }
840
+
841
+ if (this.defer_uniform_updates) {
842
+ return;
843
+ }
844
+
845
+ if (uniform.location == null) {
846
+ return;
847
+ }
848
+
849
+ this.use();
850
+ this.commitUniform(uniform);
851
+ }
852
+
853
+ // Commits the uniform to the GPU
854
+ commitUniform(uniform: CachedUniform){
855
+ let location = uniform.location;
856
+ let value = uniform.value;
857
+ if (location == null) return;
858
+ const context = this.classicContext;
859
+
860
+ switch (uniform.method) {
861
+ case '1i':
862
+ if (typeof value === 'number' || typeof value === 'boolean') context.uniform1i(location, Number(value));
863
+ break;
864
+ case '1f':
865
+ if (typeof value === 'number') context.uniform1f(location, value);
866
+ break;
867
+ case '2f':
868
+ if (typeof value === 'object') context.uniform2f(location, value[0], value[1]);
869
+ break;
870
+ case '3f':
871
+ if (typeof value === 'object') context.uniform3f(location, value[0], value[1], value[2]);
872
+ break;
873
+ case '4f':
874
+ if (typeof value === 'object') context.uniform4f(location, value[0], value[1], value[2], value[3]);
875
+ break;
876
+ case '1iv':
877
+ if (typeof value === 'object') context.uniform1iv(location, getIntegerUniformList(value));
878
+ break;
879
+ case '3iv':
880
+ if (typeof value === 'object') context.uniform3iv(location, getIntegerUniformList(value));
881
+ break;
882
+ case '1fv':
883
+ if (typeof value === 'object') context.uniform1fv(location, getFloatUniformList(value));
884
+ break;
885
+ case '2fv':
886
+ if (typeof value === 'object') context.uniform2fv(location, getFloatUniformList(value));
887
+ break;
888
+ case '3fv':
889
+ if (typeof value === 'object') context.uniform3fv(location, getFloatUniformList(value));
890
+ break;
891
+ case '4fv':
892
+ if (typeof value === 'object') context.uniform4fv(location, getFloatUniformList(value));
893
+ break;
894
+ case 'Matrix3fv':
895
+ if (typeof value === 'object') context.uniformMatrix3fv(location, false, getFloatUniformList(value));
896
+ break;
897
+ case 'Matrix4fv':
898
+ if (typeof value === 'object') context.uniformMatrix4fv(location, false, getFloatUniformList(value));
899
+ break;
900
+ }
901
+ }
902
+
903
+ // Refresh uniform locations and set to last cached values
904
+ refreshUniforms() {
905
+ if (!this.compiled) {
906
+ return;
907
+ }
908
+
909
+ if (this.defer_uniform_updates || !this.program) {
910
+ return;
911
+ }
912
+
913
+ for (var u in this.uniforms) {
914
+ let uniform = this.uniforms[u];
915
+ uniform.location = this.classicContext.getUniformLocation(this.program, u);
916
+ this.updateUniform(uniform);
917
+ }
918
+ }
919
+
920
+ refreshAttributes() {
921
+ // var len = this.gl.getProgramParameter(this.program, this.gl.ACTIVE_ATTRIBUTES);
922
+ // for (var i=0; i < len; i++) {
923
+ // var a = this.gl.getActiveAttrib(this.program, i);
924
+ // }
925
+ this.attribs = {};
926
+ }
927
+
928
+ // Get the location of a vertex attribute
929
+ attribute(name: string) {
930
+ if (!this.compiled || !this.program) {
931
+ return;
932
+ }
933
+
934
+ var attrib = (this.attribs[name] = this.attribs[name] || {name, location: this.classicContext.getAttribLocation(this.program, name)});
935
+ if (attrib.location != null) {
936
+ return attrib;
937
+ }
938
+
939
+ attrib.name = name;
940
+ attrib.location = this.classicContext.getAttribLocation(this.program, name);
941
+
942
+ // var info = this.gl.getActiveAttrib(this.program, attrib.location);
943
+ // attrib.type = info.type;
944
+ // attrib.size = info.size;
945
+
946
+ return attrib;
947
+ }
948
+
949
+ // Get shader source as string
950
+ source(type: ShaderStage) {
951
+ if (type === 'vertex') {
952
+ return this.computed_vertex_source;
953
+ }
954
+ else if (type === 'fragment') {
955
+ return this.computed_fragment_source;
956
+ }
957
+ }
958
+
959
+ // Get shader source as array of line strings
960
+ lines(type: ShaderStage) {
961
+ let source = this.source(type);
962
+ if (source) {
963
+ return source.split('\n');
964
+ }
965
+ return [];
966
+ }
967
+
968
+ // Get a specific line from shader source
969
+ line(type: ShaderStage, num: number) {
970
+ let source = this.lines(type);
971
+ if (source) {
972
+ return source[num];
973
+ }
974
+ }
975
+
976
+ // Get info on which shader block (if any) a particular line number in a shader is in
977
+ // Returns an object with the following info if a block is found: { name, line, source }
978
+ // scope: where the shader block originated, either a style name, or global such as ShaderProgram
979
+ // name: shader block name (e.g. 'color', 'position', 'global')
980
+ // num: the block number *within* local scope (e.g. if a style has multiple 'color' blocks)
981
+ // line: line number *within* the shader block (not the whole shader program), useful for error highlighting
982
+ // source: the code for the line
983
+ // NOTE: this does a bruteforce loop over the shader source and looks for shader block start/end markers
984
+ // We could track line ranges for shader blocks as they are inserted, but as this code is only used for
985
+ // error handling on compilation failure, it was simpler to keep it separate than to burden the core
986
+ // compilation path.
987
+ block(type: ShaderStage, num: number) {
988
+ let lines = this.lines(type);
989
+ let block: ShaderBlockLocation | null | undefined;
990
+ for (let i=0; i < num && i < lines.length; i++) {
991
+ let line = lines[i];
992
+ let match = line.match(/\/\/ tangram-block-start: ([A-Za-z0-9_-]+), ([A-Za-z0-9_-]+), (\d+)/);
993
+ if (match && match.length > 1) {
994
+ // mark current block
995
+ block = {
996
+ scope: match[1],
997
+ name: match[2],
998
+ num: match[3]
999
+ };
1000
+ }
1001
+ else {
1002
+ match = line.match(/\/\/ tangram-block-end: ([A-Za-z0-9_-]+), ([A-Za-z0-9_-]+), (\d+)/);
1003
+ if (match && match.length > 1) {
1004
+ block = null; // clear current block
1005
+ }
1006
+ }
1007
+
1008
+ // update line # and content
1009
+ if (block) {
1010
+ // init to -1 so that line 0 is first actual line of block code, after comment marker
1011
+ block.line = (block.line == null) ? -1 : block.line + 1;
1012
+ block.source = line;
1013
+ }
1014
+ }
1015
+ return block;
1016
+ }
1017
+
1018
+ // Returns list of available extensions from those requested
1019
+ // Sets internal #defines indicating availability of each requested extension
1020
+ checkExtensions() {
1021
+ let exts: string[] = [];
1022
+ this.extensions.forEach(name => {
1023
+ let ext = getExtension(this.classicContext, name);
1024
+ let def = `TANGRAM_EXTENSION_${name}`;
1025
+
1026
+ this.defines[def] = (ext != null);
1027
+
1028
+ if (ext) {
1029
+ exts.push(name);
1030
+ }
1031
+ else {
1032
+ log('debug', `Could not enable extension '${name}'`);
1033
+ }
1034
+ });
1035
+ return exts;
1036
+ }
1037
+
1038
+ }
1039
+
1040
+
1041
+ // Static methods and state
1042
+ ShaderProgram.id = 0; // assign each program a unique id
1043
+ ShaderProgram.current = null; // currently bound program
1044
+
1045
+ // Global config applied to all programs (duplicate properties for a specific program will take precedence)
1046
+ ShaderProgram.defines = {};
1047
+ ShaderProgram.blocks = {};
1048
+
1049
+ // Reset program and shader caches
1050
+ ShaderProgram.reset = function () {
1051
+ ShaderProgram.programs_by_source = {}; // GL program objects by exact vertex + fragment shader source
1052
+ ShaderProgram.shaders_by_source = {}; // GL shader objects by exact source
1053
+ };
1054
+ ShaderProgram.reset();
1055
+
1056
+ // Invalidate Tangram's program cache when another renderer shares the context.
1057
+ ShaderProgram.resetCurrent = function () {
1058
+ ShaderProgram.current = null;
1059
+ };
1060
+
1061
+ /** Release one wrapper's program and evict it only when its last owner retires. */
1062
+ ShaderProgram.releaseProgram = function (gl, program) {
1063
+ if (!program) return;
1064
+ const references = programReferences.get(program) || 1;
1065
+ if (references > 1) {
1066
+ programReferences.set(program, references - 1);
1067
+ return;
1068
+ }
1069
+ programReferences.delete(program);
1070
+ for (const key of Object.keys(ShaderProgram.programs_by_source)) {
1071
+ if (ShaderProgram.programs_by_source[key] === program) {
1072
+ delete ShaderProgram.programs_by_source[key];
1073
+ }
1074
+ }
1075
+ gl.deleteProgram(program);
1076
+ };
1077
+
1078
+ // Upgrade the subset of GLSL ES 1.00 syntax emitted by Tangram to GLSL ES 3.00.
1079
+ ShaderProgram.convertToWebGL2 = function (source, type) {
1080
+ source = source
1081
+ .replace(re_texture_2d, 'texture')
1082
+ .replace(re_texture_cube, 'texture');
1083
+
1084
+ if (type === 'vertex') {
1085
+ source = source
1086
+ .replace(/\battribute\b/g, 'in')
1087
+ .replace(/\bvarying\b/g, 'out');
1088
+ source = source.replace(
1089
+ /(^|\n)(\s*)in\s+([^;\n]*\ba_position\b[^;\n]*;)/,
1090
+ '$1$2layout(location = 0) in $3'
1091
+ );
1092
+ }
1093
+ else if (type === 'fragment') {
1094
+ source = source
1095
+ .replace(/\bvarying\b/g, 'in')
1096
+ .replace(re_fragment_color, 'tangram_FragColor');
1097
+ source = source.replace(
1098
+ /(precision\s+(?:lowp|mediump|highp)\s+float\s*;)/,
1099
+ '$1\nlayout(location = 0) out vec4 tangram_FragColor;'
1100
+ );
1101
+ }
1102
+
1103
+ return '#version 300 es\n' + source;
1104
+ };
1105
+
1106
+ // Turn an object of key/value pairs into single string of #define statements
1107
+ ShaderProgram.buildDefineString = function (defines) {
1108
+ var define_str = '';
1109
+ for (var d in defines) {
1110
+ const value = defines[d];
1111
+ if (value == null || value === false) {
1112
+ continue;
1113
+ }
1114
+ else if (value === true) { // booleans are simple defines with no value
1115
+ define_str += '#define ' + d + '\n';
1116
+ }
1117
+ else if (typeof value === 'number' && Math.floor(value) === value) { // int to float conversion to satisfy GLSL floats
1118
+ define_str += '#define ' + d + ' ' + value.toFixed(1) + '\n';
1119
+ }
1120
+ else if (typeof value === 'number') {
1121
+ // GLSL floats do not benefit from JavaScript's full double precision, and
1122
+ // some WebGL compilers reject excessively precise decimal literals.
1123
+ define_str += '#define ' + d + ' ' + Number(value.toPrecision(12)) + '\n';
1124
+ }
1125
+ else { // any other float or string value
1126
+ define_str += '#define ' + d + ' ' + defines[d] + '\n';
1127
+ }
1128
+ }
1129
+ return define_str;
1130
+ };
1131
+
1132
+ // Turn a list of extension names into single string of #extension statements
1133
+ ShaderProgram.buildExtensionString = function (extensions) {
1134
+ extensions = extensions || [];
1135
+ let str = '';
1136
+ extensions.forEach(ext => {
1137
+ str += `#ifdef GL_${ext}\n#extension GL_${ext} : enable\n#endif\n`;
1138
+ });
1139
+ return str;
1140
+ };
1141
+
1142
+ ShaderProgram.addBlock = function (key, ...blocks) {
1143
+ ShaderProgram.blocks[key] = ShaderProgram.blocks[key] || [];
1144
+ const sources = ShaderProgram.blocks[key];
1145
+ if (!Array.isArray(sources)) throw new TypeError(`ShaderProgram: global block '${key}' must be an array before appending`);
1146
+ sources.push(...blocks);
1147
+ };
1148
+
1149
+ function escapeRegExp(value: string) {
1150
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1151
+ }
1152
+
1153
+ // Remove all global shader blocks for a given key
1154
+ ShaderProgram.removeBlock = function (key) {
1155
+ ShaderProgram.blocks[key] = [];
1156
+ };
1157
+
1158
+ ShaderProgram.replaceBlock = function (key, ...blocks) {
1159
+ ShaderProgram.removeBlock(key);
1160
+ ShaderProgram.addBlock(key, ...blocks);
1161
+ };
1162
+
1163
+ // Compile & link a WebGL program from provided vertex and fragment shader sources
1164
+ // update a program if one is passed in. Create one if not. Alert and don't update anything if the shaders don't compile.
1165
+ ShaderProgram.updateProgram = function (gl, program, vertex_shader_source, fragment_shader_source, shader_resources = {}) {
1166
+ const use_shader_resources = Boolean(shader_resources.vertex_shader || shader_resources.fragment_shader);
1167
+ if (use_shader_resources && (!shader_resources.vertex_shader || !shader_resources.fragment_shader)) {
1168
+ throw new Error('ShaderProgram.updateProgram requires both vertex and fragment shader resources');
1169
+ }
1170
+
1171
+ // Program with this exact vertex and fragment shader sources already cached?
1172
+ let key = hashString(gl._tangram_id + '::' + vertex_shader_source + '::' + fragment_shader_source);
1173
+ if (!use_shader_resources && ShaderProgram.programs_by_source[key]) {
1174
+ log('trace', 'Reusing identical source GL program object');
1175
+ const cached = ShaderProgram.programs_by_source[key];
1176
+ if (cached !== program) {
1177
+ ShaderProgram.releaseProgram(gl, program);
1178
+ programReferences.set(cached, (programReferences.get(cached) || 0) + 1);
1179
+ }
1180
+ return cached;
1181
+ }
1182
+
1183
+ var vertex_shader = use_shader_resources ? shader_resources.vertex_shader?.handle :
1184
+ ShaderProgram.createShader(gl, vertex_shader_source, gl.VERTEX_SHADER);
1185
+ var fragment_shader = use_shader_resources ? shader_resources.fragment_shader?.handle :
1186
+ ShaderProgram.createShader(gl, fragment_shader_source, gl.FRAGMENT_SHADER);
1187
+
1188
+ gl.useProgram(null);
1189
+ // Relinking a shared program would also change every other wrapper using it.
1190
+ ShaderProgram.releaseProgram(gl, program);
1191
+ program = gl.createProgram();
1192
+ if (!program) throw new Error('ShaderProgram: WebGL program allocation failed');
1193
+ programReferences.set(program, 1);
1194
+
1195
+ if (vertex_shader == null || fragment_shader == null) {
1196
+ return program;
1197
+ }
1198
+
1199
+ gl.attachShader(program, vertex_shader);
1200
+ gl.attachShader(program, fragment_shader);
1201
+
1202
+ // Require position to be at attribute location 0
1203
+ // Attribute 0 should never be disabled (per GL best practices). All of our shader programs have an `a_position`
1204
+ // attribute, and it's customary for the vertex position to be the first attribute, so we enforce that here.
1205
+ // This can avoid unexpected/undefined interaction between static and dynamic attributes in Safari, and
1206
+ // possible warnings/errors in other browsers.
1207
+ // See https://stackoverflow.com/questions/20305231/webgl-warning-attribute-0-is-disabled-this-has-significant-performance-penalt/20923946
1208
+ gl.bindAttribLocation(program, 0, 'a_position');
1209
+
1210
+ gl.linkProgram(program);
1211
+
1212
+ // TODO: reference count and delete shader objects when no programs reference them
1213
+
1214
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
1215
+ let message = new Error(
1216
+ `WebGL program error:
1217
+ PROGRAM_INFO_LOG: ${gl.getProgramInfoLog(program)}
1218
+ VERTEX_SHADER_INFO_LOG: ${gl.getShaderInfoLog(vertex_shader)}
1219
+ FRAGMENT_SHADER_INFO_LOG: ${gl.getShaderInfoLog(fragment_shader)}
1220
+ VALIDATE_STATUS: ${gl.getProgramParameter(program, gl.VALIDATE_STATUS)}
1221
+ ERROR: ${gl.getError()}
1222
+ --- Vertex Shader ---
1223
+ ${vertex_shader_source}
1224
+ --- Fragment Shader ---
1225
+ ${fragment_shader_source}`);
1226
+
1227
+ ShaderProgram.releaseProgram(gl, program);
1228
+ throw Object.assign(new Error(String(message)), { type: 'program' });
1229
+ }
1230
+
1231
+ if (!use_shader_resources) {
1232
+ ShaderProgram.programs_by_source[key] = program; // cache by exact source
1233
+ }
1234
+ return program;
1235
+ };
1236
+
1237
+ // Compile a vertex or fragment shader from provided source
1238
+ ShaderProgram.createShader = function (gl, source, stype) {
1239
+ // Program with identical vertex and fragment shader sources already cached?
1240
+ let key = hashString(gl._tangram_id + '::' + source);
1241
+ if (ShaderProgram.shaders_by_source[key]) {
1242
+ log('trace', 'Reusing identical source GL shader object');
1243
+ return ShaderProgram.shaders_by_source[key];
1244
+ }
1245
+
1246
+ let shader = gl.createShader(stype);
1247
+ if (!shader) throw new Error('ShaderProgram: WebGL shader allocation failed');
1248
+
1249
+ gl.shaderSource(shader, source);
1250
+ gl.compileShader(shader);
1251
+
1252
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
1253
+ let type = (stype === gl.VERTEX_SHADER ? 'vertex' : 'fragment');
1254
+ let message = gl.getShaderInfoLog(shader);
1255
+ let errors = parseShaderErrors(message);
1256
+ throw Object.assign(new Error(String(message)), { type, errors });
1257
+ }
1258
+
1259
+ ShaderProgram.shaders_by_source[key] = shader; // cache by exact source
1260
+ return shader;
1261
+ };
1262
+
1263
+ function destroyShaderResource(shader: Pick<Shader, 'destroy'> | null | undefined) {
1264
+ if (shader && typeof shader.destroy === 'function') {
1265
+ shader.destroy();
1266
+ }
1267
+ }
1268
+
1269
+ /** Structural classic boundary also accepts deterministic WebGL fixtures. */
1270
+ function isShaderContext(context: object | null): context is ShaderContext {
1271
+ return context !== null && 'useProgram' in context && typeof context.useProgram === 'function';
1272
+ }
1273
+ /** Only native stage diagnostics carry a parsed error list. */
1274
+ function isShaderFailure(error: unknown): error is {type: ShaderStage; errors: ShaderDiagnostic[]} {
1275
+ return error instanceof Object && 'type' in error && (error.type === 'vertex' || error.type === 'fragment') &&
1276
+ 'errors' in error && Array.isArray(error.errors);
1277
+ }
1278
+
1279
+ /** Numeric uniforms are distinct from named texture samplers. */
1280
+ function isNumericUniform(value: unknown): value is UniformValue {
1281
+ return typeof value === 'number' || typeof value === 'boolean' ||
1282
+ Array.isArray(value) && value.every(component => typeof component === 'number');
1283
+ }
1284
+
1285
+ /** Metadata-only worker records cannot bind; portable samplers need no classic bind method. */
1286
+ function isSamplerTexture(texture: object): texture is SamplerTexture {
1287
+ return 'getResource' in texture && typeof texture.getResource === 'function' ||
1288
+ 'bind' in texture && typeof texture.bind === 'function';
1289
+ }
1290
+
1291
+ /** Preserve existing numeric arrays and ArrayBuffer-backed float views without per-draw copies. */
1292
+ function getFloatUniformList(value: Exclude<UniformValue, number | boolean>): Float32List {
1293
+ if (Array.isArray(value)) return value;
1294
+ if (isFloat32UniformList(value)) return value;
1295
+ return Array.from(value);
1296
+ }
1297
+ /** Preserve existing numeric arrays and integer views used by classic vector uniforms. */
1298
+ function getIntegerUniformList(value: Exclude<UniformValue, number | boolean>): Int32List {
1299
+ if (Array.isArray(value)) return value;
1300
+ if (isInt32UniformList(value)) return value;
1301
+ return Array.from(value);
1302
+ }
1303
+ /** WebGL accepts non-shared float storage; the check narrows the buffer ownership parameter. */
1304
+ function isFloat32UniformList(value: unknown): value is Float32Array<ArrayBuffer> {
1305
+ return value instanceof Float32Array && value.buffer instanceof ArrayBuffer;
1306
+ }
1307
+ /** WebGL accepts non-shared integer storage; the check narrows the buffer ownership parameter. */
1308
+ function isInt32UniformList(value: unknown): value is Int32Array<ArrayBuffer> {
1309
+ return value instanceof Int32Array && value.buffer instanceof ArrayBuffer;
1310
+ }