@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.
- package/README.md +59 -2
- package/dist/core.js +36923 -0
- package/dist/core.js.map +6 -0
- package/dist/index.js +32 -0
- package/dist/loaders-gl-worker.js +82 -0
- package/dist/loaders-gl-worker.js.map +6 -0
- package/dist/map-logic.js +2 -0
- package/dist/map-logic.js.map +6 -0
- package/dist/projected-basemaps-worker.js +3 -0
- package/dist/projected-basemaps-worker.js.map +6 -0
- package/dist/shader-modules.js +141 -0
- package/dist/shader-modules.js.map +6 -0
- package/dist/style-schema.js +126 -0
- package/dist/style-schema.js.map +6 -0
- package/dist/tangram-style.schema.json +882 -0
- package/dist/tangram.debug.mjs +37139 -0
- package/dist/tangram.debug.mjs.map +6 -0
- package/dist/tangram.min.mjs +4950 -0
- package/dist/types/builders/earcut.d.ts +15 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/gl/constants.d.ts +2 -0
- package/dist/types/gl/globe_mesh.d.ts +48 -0
- package/dist/types/gl/glsl.d.ts +17 -0
- package/dist/types/gl/vertex-constants.d.ts +4 -0
- package/dist/types/gl/vertex-types.d.ts +34 -0
- package/dist/types/gl/vertex_data.d.ts +32 -0
- package/dist/types/gl/vertex_elements.d.ts +8 -0
- package/dist/types/gl/vertex_layout.d.ts +32 -0
- package/dist/types/glsl.d.ts +14 -0
- package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
- package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
- package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/lights/light-definitions.d.ts +121 -0
- package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
- package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
- package/dist/types/map-logic/globe-terrain.d.ts +54 -0
- package/dist/types/map-logic/index.d.ts +33 -0
- package/dist/types/map-logic/label-identity.d.ts +22 -0
- package/dist/types/map-logic/label-placement.d.ts +32 -0
- package/dist/types/map-logic/screen-bounds.d.ts +15 -0
- package/dist/types/map-logic/terrain-data.d.ts +55 -0
- package/dist/types/map-logic/terrain-error.d.ts +51 -0
- package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
- package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
- package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
- package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
- package/dist/types/map-logic/terrain-query.d.ts +33 -0
- package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
- package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
- package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
- package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
- package/dist/types/map-logic/tile-residency.d.ts +17 -0
- package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
- package/dist/types/procedures/js-yaml.d.ts +15 -0
- package/dist/types/procedures/mesh-projector.d.ts +107 -0
- package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
- package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
- package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
- package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
- package/dist/types/procedures/terrain-draping.d.ts +165 -0
- package/dist/types/procedures/terrain-globe.d.ts +6 -0
- package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
- package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
- package/dist/types/procedures/web-mercator-math.d.ts +4 -0
- package/dist/types/scene/globals.d.ts +11 -0
- package/dist/types/scene/ground_footprint.d.ts +16 -0
- package/dist/types/scene/host_frame.d.ts +54 -0
- package/dist/types/scene/projection_math.d.ts +52 -0
- package/dist/types/scene/scene-resource-types.d.ts +177 -0
- package/dist/types/scene/scene_bundle.d.ts +69 -0
- package/dist/types/scene/visibility_adapter.d.ts +106 -0
- package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
- package/dist/types/selection/terrain_surface.d.ts +149 -0
- package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
- package/dist/types/shader-modules/height-decode.d.ts +13 -0
- package/dist/types/shader-modules/hillshade.d.ts +13 -0
- package/dist/types/shader-modules/index.d.ts +6 -0
- package/dist/types/shader-modules/planar.d.ts +12 -0
- package/dist/types/shader-modules/sphere-map.d.ts +13 -0
- package/dist/types/shader-modules/triplanar.d.ts +15 -0
- package/dist/types/sources/decoded_tile_store.d.ts +95 -0
- package/dist/types/sources/tile_source_metadata.d.ts +32 -0
- package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
- package/dist/types/tile/tile_id.d.ts +37 -0
- package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
- package/dist/types/types.d.ts +293 -0
- package/dist/types/utils/geo.d.ts +57 -0
- package/dist/types/utils/hash.d.ts +1 -0
- package/dist/types/utils/log.d.ts +27 -0
- package/dist/types/utils/props.d.ts +6 -0
- package/dist/types/utils/subscribe.d.ts +10 -0
- package/dist/types/utils/thread.d.ts +6 -0
- package/dist/types/utils/urls.d.ts +26 -0
- package/dist/types/utils/utils.d.ts +26 -0
- package/dist/types/utils/version.d.ts +2 -0
- package/dist/types/utils/worker-types.d.ts +68 -0
- package/dist/types/utils/worker_broker.d.ts +3 -0
- package/package.json +103 -3
- package/src/builders/common.ts +41 -0
- package/src/builders/earcut.d.ts +15 -0
- package/src/builders/geometry-types.ts +60 -0
- package/src/builders/points.ts +115 -0
- package/src/builders/polygons.ts +225 -0
- package/src/builders/polylines.ts +682 -0
- package/src/builders/wireframe.ts +31 -0
- package/src/core.d.ts +29 -0
- package/src/core.ts +33 -0
- package/src/experimental/loaders-gl-worker.ts +19 -0
- package/src/experimental/projected-basemaps-worker.ts +13 -0
- package/src/experimental/projected-mesh-cache.ts +137 -0
- package/src/experimental/projected-mesh-domain.ts +110 -0
- package/src/experimental/projected-mesh-refinement.ts +193 -0
- package/src/experimental/projected-mesh.ts +399 -0
- package/src/experimental/projected-normal.ts +26 -0
- package/src/experimental/projected-symbol-mesh.ts +74 -0
- package/src/gl/constants.ts +18 -0
- package/src/gl/context.ts +103 -0
- package/src/gl/extensions.ts +30 -0
- package/src/gl/globe_mesh.ts +285 -0
- package/src/gl/glsl.ts +257 -0
- package/src/gl/mesh-types.ts +56 -0
- package/src/gl/render_state.ts +111 -0
- package/src/gl/shader_program.ts +1310 -0
- package/src/gl/texture.ts +814 -0
- package/src/gl/uniform_buffer.ts +340 -0
- package/src/gl/vao.ts +154 -0
- package/src/gl/vbo_mesh.ts +349 -0
- package/src/gl/vertex-constants.ts +19 -0
- package/src/gl/vertex-types.ts +35 -0
- package/src/gl/vertex_data.ts +126 -0
- package/src/gl/vertex_elements.ts +54 -0
- package/src/gl/vertex_layout.ts +241 -0
- package/src/glsl.d.ts +14 -0
- package/src/gpu/luma_device_renderer.ts +579 -0
- package/src/gpu/pixel_readback.ts +51 -0
- package/src/gpu/resource_lifecycle.ts +32 -0
- package/src/gpu/tangram_gpu_backend.ts +195 -0
- package/src/index.d.ts +155 -0
- package/src/index.ts +105 -0
- package/src/labels/collision-types.ts +94 -0
- package/src/labels/collision.ts +273 -0
- package/src/labels/collision_grid.ts +61 -0
- package/src/labels/intersect.ts +30 -0
- package/src/labels/label-types.ts +91 -0
- package/src/labels/label.ts +207 -0
- package/src/labels/label_line.ts +801 -0
- package/src/labels/label_point.ts +131 -0
- package/src/labels/main-pass-types.ts +73 -0
- package/src/labels/main_pass.ts +221 -0
- package/src/labels/point_anchor.ts +100 -0
- package/src/labels/point_placement.ts +150 -0
- package/src/labels/projected-pass.ts +259 -0
- package/src/labels/repeat_group.ts +72 -0
- package/src/lights/ambient_light.glsl +18 -0
- package/src/lights/directional_light.glsl +39 -0
- package/src/lights/geographic-lights.ts +21 -0
- package/src/lights/light-definitions.ts +285 -0
- package/src/lights/light.ts +590 -0
- package/src/lights/lighting-uniforms.ts +80 -0
- package/src/lights/lighting-wgsl.ts +116 -0
- package/src/lights/material.glsl +237 -0
- package/src/lights/material.ts +145 -0
- package/src/lights/native-falloff.ts +37 -0
- package/src/lights/point_light.glsl +96 -0
- package/src/lights/spot_light.glsl +111 -0
- package/src/map-logic/draped-terrain-bounds.ts +71 -0
- package/src/map-logic/globe-terrain-preparation.ts +217 -0
- package/src/map-logic/globe-terrain.ts +296 -0
- package/src/map-logic/index.ts +37 -0
- package/src/map-logic/label-identity.ts +36 -0
- package/src/map-logic/label-placement.ts +117 -0
- package/src/map-logic/screen-bounds.ts +34 -0
- package/src/map-logic/terrain-data.ts +107 -0
- package/src/map-logic/terrain-error.ts +134 -0
- package/src/map-logic/terrain-ground-topology.ts +164 -0
- package/src/map-logic/terrain-height-field.ts +254 -0
- package/src/map-logic/terrain-navigation.ts +50 -0
- package/src/map-logic/terrain-neighborhood.ts +43 -0
- package/src/map-logic/terrain-query.ts +51 -0
- package/src/map-logic/terrain-resource-budget.ts +124 -0
- package/src/map-logic/terrain-transition-topology.ts +164 -0
- package/src/map-logic/tile-build-queue.ts +110 -0
- package/src/map-logic/tile-cache-policy.ts +89 -0
- package/src/map-logic/tile-residency.ts +50 -0
- package/src/map-logic/triangle-query-index.ts +265 -0
- package/src/procedures/js-yaml.d.ts +15 -0
- package/src/procedures/mesh-projector.ts +191 -0
- package/src/procedures/mlt-loaders.ts +55 -0
- package/src/procedures/mvt-dependencies.d.ts +20 -0
- package/src/procedures/mvt-legacy.ts +150 -0
- package/src/procedures/mvt-loaders.ts +53 -0
- package/src/procedures/mvt-parser.ts +48 -0
- package/src/procedures/mvt-properties.ts +36 -0
- package/src/procedures/mvt-tile-provider.ts +72 -0
- package/src/procedures/pmtiles-loader.ts +75 -0
- package/src/procedures/projected-coordinate-transform.ts +79 -0
- package/src/procedures/projection-batch-executor.ts +140 -0
- package/src/procedures/scene-yaml-legacy.ts +16 -0
- package/src/procedures/scene-yaml-loaders.ts +10 -0
- package/src/procedures/terrain-draping.ts +713 -0
- package/src/procedures/terrain-globe.ts +32 -0
- package/src/procedures/terrain-styled-topology.ts +184 -0
- package/src/procedures/web-mercator-legacy.ts +24 -0
- package/src/procedures/web-mercator-math.ts +50 -0
- package/src/scene/camera.ts +298 -0
- package/src/scene/camera_base.ts +116 -0
- package/src/scene/classic_scene.ts +20 -0
- package/src/scene/external_camera.ts +101 -0
- package/src/scene/globals.ts +120 -0
- package/src/scene/ground_footprint.ts +139 -0
- package/src/scene/host_frame.ts +326 -0
- package/src/scene/projected_tile_lod.ts +125 -0
- package/src/scene/projection_math.ts +142 -0
- package/src/scene/projection_shaders.ts +183 -0
- package/src/scene/renderer.ts +516 -0
- package/src/scene/scene-resource-types.ts +174 -0
- package/src/scene/scene.ts +2378 -0
- package/src/scene/scene_bundle.ts +304 -0
- package/src/scene/scene_debug.ts +129 -0
- package/src/scene/scene_loader.ts +402 -0
- package/src/scene/scene_worker.ts +528 -0
- package/src/scene/view.ts +683 -0
- package/src/scene/visibility_adapter.ts +319 -0
- package/src/scene/worker_url.ts +15 -0
- package/src/selection/draped-terrain-surface.ts +377 -0
- package/src/selection/selection-types.ts +79 -0
- package/src/selection/selection.ts +542 -0
- package/src/selection/selection_fragment.glsl +18 -0
- package/src/selection/selection_globals.glsl +9 -0
- package/src/selection/selection_pixels.ts +59 -0
- package/src/selection/selection_vertex.glsl +15 -0
- package/src/selection/terrain_surface.ts +298 -0
- package/src/shader-modules/globe-horizon.ts +52 -0
- package/src/shader-modules/height-decode.ts +41 -0
- package/src/shader-modules/hillshade.ts +70 -0
- package/src/shader-modules/index.ts +10 -0
- package/src/shader-modules/planar.ts +24 -0
- package/src/shader-modules/sphere-map.ts +38 -0
- package/src/shader-modules/triplanar.ts +45 -0
- package/src/sources/aligned_tile_source.ts +122 -0
- package/src/sources/data_source.ts +655 -0
- package/src/sources/decoded_tile_store.ts +245 -0
- package/src/sources/geojson.ts +300 -0
- package/src/sources/mvt.ts +247 -0
- package/src/sources/raster.ts +341 -0
- package/src/sources/shared_tile_source_adapter.ts +117 -0
- package/src/sources/sources.ts +10 -0
- package/src/sources/tile_source_adapter.ts +84 -0
- package/src/sources/tile_source_metadata.ts +65 -0
- package/src/sources/tile_source_state.ts +112 -0
- package/src/sources/topojson.ts +90 -0
- package/src/styles/feature_annotations.ts +16 -0
- package/src/styles/filter.ts +190 -0
- package/src/styles/geometry-style-types.ts +209 -0
- package/src/styles/globe_visibility_wgsl.ts +22 -0
- package/src/styles/layer-types.ts +45 -0
- package/src/styles/layer.ts +586 -0
- package/src/styles/lines/dasharray.ts +49 -0
- package/src/styles/lines/lines.ts +707 -0
- package/src/styles/lines/lines_wgsl.ts +224 -0
- package/src/styles/points/point-runtime-types.ts +125 -0
- package/src/styles/points/points.ts +1079 -0
- package/src/styles/points/points_fragment.glsl +126 -0
- package/src/styles/points/points_vertex.glsl +230 -0
- package/src/styles/points/points_wgsl.ts +189 -0
- package/src/styles/polygons/polygons.ts +268 -0
- package/src/styles/polygons/polygons_fragment.glsl +173 -0
- package/src/styles/polygons/polygons_vertex.glsl +213 -0
- package/src/styles/polygons/polygons_wgsl.ts +127 -0
- package/src/styles/property-types.ts +125 -0
- package/src/styles/raster/raster.ts +33 -0
- package/src/styles/raster/raster_globals.glsl +46 -0
- package/src/styles/style-mixing-types.ts +101 -0
- package/src/styles/style-runtime-types.ts +220 -0
- package/src/styles/style-schema.ts +133 -0
- package/src/styles/style.ts +1033 -0
- package/src/styles/style_globals.glsl +69 -0
- package/src/styles/style_manager.ts +464 -0
- package/src/styles/style_parser.ts +620 -0
- package/src/styles/text/font_manager.ts +180 -0
- package/src/styles/text/fontfaceobserver.d.ts +10 -0
- package/src/styles/text/text-types.ts +220 -0
- package/src/styles/text/text.ts +378 -0
- package/src/styles/text/text_canvas.ts +773 -0
- package/src/styles/text/text_labels.ts +436 -0
- package/src/styles/text/text_segments.ts +142 -0
- package/src/styles/text/text_settings.ts +228 -0
- package/src/styles/text/text_wgsl.ts +159 -0
- package/src/styles/text/text_wrap.ts +194 -0
- package/src/tile/globe_tile_preload.ts +40 -0
- package/src/tile/tangram_tileset_2d.ts +108 -0
- package/src/tile/tile-build-types.ts +155 -0
- package/src/tile/tile.ts +807 -0
- package/src/tile/tile_id.ts +172 -0
- package/src/tile/tile_manager.ts +889 -0
- package/src/tile/tile_pyramid.ts +119 -0
- package/src/tile/tile_resource_cache.ts +91 -0
- package/src/tile/tile_traversal_adapter.ts +132 -0
- package/src/types.ts +332 -0
- package/src/utils/debounce.ts +17 -0
- package/src/utils/debug_settings.ts +43 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/functions.ts +95 -0
- package/src/utils/geo.ts +364 -0
- package/src/utils/hash.ts +14 -0
- package/src/utils/log.ts +108 -0
- package/src/utils/media_capture.ts +193 -0
- package/src/utils/merge.ts +44 -0
- package/src/utils/obb.ts +134 -0
- package/src/utils/props.ts +43 -0
- package/src/utils/slice.ts +14 -0
- package/src/utils/subscribe.ts +58 -0
- package/src/utils/task.ts +154 -0
- package/src/utils/thread.ts +26 -0
- package/src/utils/urls.ts +223 -0
- package/src/utils/utils.ts +310 -0
- package/src/utils/vector.ts +197 -0
- package/src/utils/version.ts +8 -0
- package/src/utils/worker-types.ts +79 -0
- 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
|
+
}
|