Skip to content

Commit d84fa78

Browse files
stepankuzmingithub-actions[bot]
authored andcommitted
Refactor DevTools into a control
GitOrigin-RevId: dac9854910aaa41f78ee3f0573c5a8323c79a1fb
1 parent 4747bec commit d84fa78

16 files changed

Lines changed: 1378 additions & 711 deletions

3d-style/render/draw_building.ts

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {mat4} from 'gl-matrix';
77
import StencilMode from '../../src/gl/stencil_mode';
88
import {getMetersPerPixelAtLatitude} from '../../src/geo/mercator_coordinate';
99
import {Debug} from '../../src/util/debug';
10-
import {DevTools} from '../../src/ui/devtools';
1110
import {drawGroundEffect as fillExtrusionDrawGroundEffect, GroundEffectProperties, frustumCullShadowCaster, computeFrontCutoffParams} from '../../src/render/draw_fill_extrusion';
1211
import Color from '../../src/style-spec/util/color';
1312
import ColorMode from '../../src/gl/color_mode';
@@ -199,40 +198,6 @@ function drawTiles(params: DrawParams) {
199198
}
200199
}
201200

202-
// Debug settings for rendering of buildings
203-
204-
let drawBuildingsDebugParams: DrawBuildingsDebugParams | null = null;
205-
206-
class DrawBuildingsDebugParams {
207-
public showNormals: boolean = false;
208-
public drawGroundAO: boolean = true;
209-
public drawShadowPass: boolean = true;
210-
public drawTranslucentPass: boolean = true;
211-
212-
public static getOrCreateInstance(painter: Painter): DrawBuildingsDebugParams {
213-
214-
if (!drawBuildingsDebugParams) {
215-
drawBuildingsDebugParams = new DrawBuildingsDebugParams(painter);
216-
}
217-
return drawBuildingsDebugParams;
218-
}
219-
220-
constructor(painter: Painter) {
221-
DevTools.addParameter(this, 'drawTranslucentPass', 'Buildings', {label: 'Draw Translucent Pass'}, () => {
222-
painter.style.map.triggerRepaint();
223-
});
224-
DevTools.addParameter(this, 'drawShadowPass', 'Buildings', {label: 'Draw Shadow Pass'}, () => {
225-
painter.style.map.triggerRepaint();
226-
});
227-
DevTools.addParameter(this, 'showNormals', 'Buildings', {label: 'Show normals'}, () => {
228-
painter.style.map.triggerRepaint();
229-
});
230-
DevTools.addParameter(this, 'drawGroundAO', 'Buildings', {label: 'Ground AO'}, () => {
231-
painter.style.map.triggerRepaint();
232-
});
233-
}
234-
}
235-
236201
function drawGroundEffect(painter: Painter, source: SourceCache, layer: BuildingStyleLayer, coords: Array<OverscaledTileID>, aoPass: boolean, opacity: number, aoIntensity: number, aoRadius: number, floodLightIntensity: number, floodLightColor: [number, number, number], attenuationFactor: number, replacementActive: boolean, renderNeighbors: boolean, frontCutoffParams?: [number, number, number]) {
237202
const lerp = (a: number, b: number, t: number) => { return (1 - t) * a + t * b; };
238203

@@ -316,10 +281,9 @@ function draw(painter: Painter, source: SourceCache, layer: BuildingStyleLayer,
316281
}
317282

318283
Debug.run(() => {
319-
const debugParams = DrawBuildingsDebugParams.getOrCreateInstance(painter);
320-
aoEnabled = aoEnabled && debugParams.drawGroundAO;
321-
castsShadowsEnabled = castsShadowsEnabled && debugParams.drawShadowPass;
322-
drawLayer = drawLayer && debugParams.drawTranslucentPass;
284+
aoEnabled = aoEnabled && painter._debugParams.buildingsDrawGroundAO;
285+
castsShadowsEnabled = castsShadowsEnabled && painter._debugParams.buildingsDrawShadowPass;
286+
drawLayer = drawLayer && painter._debugParams.buildingsDrawTranslucentPass;
323287
});
324288

325289
if (!painter.shadowRenderer) {
@@ -374,10 +338,9 @@ function draw(painter: Painter, source: SourceCache, layer: BuildingStyleLayer,
374338
if (painter.shadowRenderer && painter.shadowRenderer.useNormalOffset) {
375339
definesForPass = definesForPass.concat("NORMAL_OFFSET");
376340
}
377-
// Apply debug settinggs. Stripped out in production.
341+
378342
Debug.run(() => {
379-
const debugParams = DrawBuildingsDebugParams.getOrCreateInstance(painter);
380-
if (debugParams.showNormals) {
343+
if (painter._debugParams.buildingsShowNormals) {
381344
definesForPass = definesForPass.concat("DEBUG_SHOW_NORMALS");
382345
}
383346
});

3d-style/render/shadow_renderer.ts

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ import Color from '../../src/style-spec/util/color';
88
import {FreeCamera} from '../../src/ui/free_camera';
99
import {mercatorZfromAltitude, tileToMeter} from '../../src/geo/mercator_coordinate';
1010
import {cartesianPositionToSpherical, sphericalPositionToCartesian, clamp, linearVec3TosRGB} from '../../src/util/util';
11-
import {DevTools} from '../../src/ui/devtools';
1211
import {defaultShadowUniformValues} from '../render/shadow_uniforms';
1312
import TextureSlots from './texture_slots';
1413
import assert from 'assert';
1514
import {mat4, vec3} from 'gl-matrix';
1615
import {groundShadowUniformValues} from './program/ground_shadow_program';
1716
import EXTENT from '../../src/style-spec/data/extent';
1817
import {getCutoffParams} from '../../src/render/cutoff';
18+
import {Debug} from '../../src/util/debug';
1919

2020
import type {vec4} from 'gl-matrix';
21+
import type {DevToolsFolder} from '../../src/ui/control/devtools';
2122
import type Lights from '../style/lights';
2223
import type {OverscaledTileID, UnwrappedTileID} from '../../src/source/tile_id';
2324
import type Transform from '../../src/geo/transform';
@@ -70,12 +71,6 @@ export type TileShadowVolume = {
7071

7172
type ShadowNormalOffsetMode = 'vector-tile' | 'model-tile';
7273

73-
const shadowParameters = {
74-
cascadeCount: 2,
75-
normalOffset: 3,
76-
shadowMapResolution: 2048
77-
};
78-
7974
function lerpClamp(x: number, x1: number, x2: number, y1: number, y2: number) {
8075
const t = clamp((x - x1) / (x2 - x1), 0, 1);
8176
return (1 - t) * y1 + t * y2;
@@ -173,6 +168,13 @@ export class ShadowRenderer {
173168
useNormalOffset: boolean;
174169

175170
_forceDisable: boolean;
171+
_devtoolsFolder: DevToolsFolder | null;
172+
173+
_shadowParameters: {
174+
cascadeCount: number,
175+
normalOffset: number,
176+
shadowMapResolution: number
177+
};
176178

177179
constructor(painter: Painter) {
178180
this.painter = painter;
@@ -185,17 +187,25 @@ export class ShadowRenderer {
185187
this._depthMode = new DepthMode(painter.context.gl.LEQUAL, DepthMode.ReadWrite, [0, 1]);
186188
this._uniformValues = defaultShadowUniformValues();
187189
this._forceDisable = false;
190+
this._devtoolsFolder = null;
188191

189192
this.useNormalOffset = false;
190193

191-
DevTools.addParameter(this, '_forceDisable', 'Shadows', {label: 'forceDisable'}, () => { this.painter.style.map.triggerRepaint(); });
192-
DevTools.addParameter(shadowParameters, 'cascadeCount', 'Shadows', {min: 1, max: 2, step: 1});
193-
DevTools.addParameter(shadowParameters, 'normalOffset', 'Shadows', {min: 0, max: 10, step: 0.05});
194-
DevTools.addParameter(shadowParameters, 'shadowMapResolution', 'Shadows', {min: 32, max: 2048, step: 32});
195-
DevTools.addBinding(this, '_numCascadesToRender', 'Shadows', {readonly: true, label: 'numCascadesToRender'});
194+
this._shadowParameters = {
195+
cascadeCount: 2,
196+
normalOffset: 3,
197+
shadowMapResolution: 2048
198+
};
196199
}
197200

198201
destroy() {
202+
Debug.run(() => {
203+
if (this.painter._devtools) {
204+
this.painter._devtools.removeFolder('Shadows');
205+
}
206+
this._devtoolsFolder = null;
207+
});
208+
199209
for (const cascade of this._cascades) {
200210
cascade.texture.destroy();
201211
cascade.framebuffer.destroy();
@@ -207,6 +217,18 @@ export class ShadowRenderer {
207217
updateShadowParameters(transform: Transform, directionalLight?: Lights<Directional> | null) {
208218
const painter = this.painter;
209219

220+
Debug.run(() => {
221+
if (painter._devtools && !this._devtoolsFolder) {
222+
const folder = painter._devtools.addFolder('Shadows');
223+
folder.addBinding(this, '_forceDisable', {label: 'forceDisable'}, () => painter.style.map.triggerRepaint());
224+
folder.addBinding(this._shadowParameters, 'cascadeCount', {min: 1, max: 2, step: 1});
225+
folder.addBinding(this._shadowParameters, 'normalOffset', {min: 0, max: 10, step: 0.05});
226+
folder.addBinding(this._shadowParameters, 'shadowMapResolution', {min: 32, max: 2048, step: 32});
227+
folder.addReadonly(this, '_numCascadesToRender', {label: 'numCascadesToRender'});
228+
this._devtoolsFolder = folder;
229+
}
230+
});
231+
210232
this._enabled = false;
211233
this._drawShadowAfterLayer = -1;
212234
this._receivers.clear();
@@ -252,12 +274,12 @@ export class ShadowRenderer {
252274
}
253275

254276
const context = painter.context;
255-
const width = shadowParameters.shadowMapResolution;
256-
const height = shadowParameters.shadowMapResolution;
277+
const width = this._shadowParameters.shadowMapResolution;
278+
const height = this._shadowParameters.shadowMapResolution;
257279

258-
if (this._cascades.length === 0 || shadowParameters.shadowMapResolution !== this._cascades[0].texture.size[0]) {
280+
if (this._cascades.length === 0 || this._shadowParameters.shadowMapResolution !== this._cascades[0].texture.size[0]) {
259281
this._cascades = [];
260-
for (let i = 0; i < shadowParameters.cascadeCount; ++i) {
282+
for (let i = 0; i < this._shadowParameters.cascadeCount; ++i) {
261283
const gl = context.gl;
262284
const fbo = context.createFramebuffer(width, height, 0, 'texture');
263285
const depthTexture = new Texture(context, {width, height, data: null}, gl.DEPTH_COMPONENT16);
@@ -299,7 +321,7 @@ export class ShadowRenderer {
299321
let near = transform.height / 50.0;
300322
let far = 1.0;
301323

302-
if (shadowParameters.cascadeCount === 1) {
324+
if (this._shadowParameters.cascadeCount === 1) {
303325
far = shadowCutoutDist;
304326
} else {
305327
if (cascadeIndex === 0) {
@@ -310,7 +332,7 @@ export class ShadowRenderer {
310332
}
311333
}
312334

313-
const [matrix, radius] = createLightMatrix(transform, this.shadowDirection, near, far, shadowParameters.shadowMapResolution, verticalRange);
335+
const [matrix, radius] = createLightMatrix(transform, this.shadowDirection, near, far, this._shadowParameters.shadowMapResolution, verticalRange);
314336
cascade.scale = transform.scale;
315337
cascade.matrix = matrix;
316338
cascade.boundingSphereRadius = radius;
@@ -323,8 +345,8 @@ export class ShadowRenderer {
323345
this._uniformValues['u_fade_range'] = [this._cascades[fadeRangeIdx].far * 0.75, this._cascades[fadeRangeIdx].far];
324346
this._uniformValues['u_shadow_intensity'] = shadowIntensity;
325347
this._uniformValues['u_shadow_direction'] = [this.shadowDirection[0], this.shadowDirection[1], this.shadowDirection[2]];
326-
this._uniformValues['u_shadow_texel_size'] = 1 / shadowParameters.shadowMapResolution;
327-
this._uniformValues['u_shadow_map_resolution'] = shadowParameters.shadowMapResolution;
348+
this._uniformValues['u_shadow_texel_size'] = 1 / this._shadowParameters.shadowMapResolution;
349+
this._uniformValues['u_shadow_map_resolution'] = this._shadowParameters.shadowMapResolution;
328350
this._uniformValues['u_shadowmap_0'] = TextureSlots.ShadowMap0;
329351
this._uniformValues['u_shadowmap_1'] = TextureSlots.ShadowMap0 + 1;
330352

@@ -373,7 +395,7 @@ export class ShadowRenderer {
373395
// shadows.
374396
this._numCascadesToRender = this._receivers.computeRequiredCascades(painter.transform.getFrustum(0), painter.transform.worldSize, this._cascades);
375397

376-
context.viewport.set([0, 0, shadowParameters.shadowMapResolution, shadowParameters.shadowMapResolution]);
398+
context.viewport.set([0, 0, this._shadowParameters.shadowMapResolution, this._shadowParameters.shadowMapResolution]);
377399

378400
for (let cascade = 0; cascade < this._numCascadesToRender; ++cascade) {
379401
painter.currentShadowCascade = cascade;
@@ -492,7 +514,7 @@ export class ShadowRenderer {
492514

493515
if (this.useNormalOffset) {
494516
const meterInTiles = tileToMeter(unwrappedTileID.canonical);
495-
const texelScale = 2.0 / transform.tileSize * EXTENT / shadowParameters.shadowMapResolution;
517+
const texelScale = 2.0 / transform.tileSize * EXTENT / this._shadowParameters.shadowMapResolution;
496518
const shadowTexelInTileCoords0 = texelScale * this._cascades[0].boundingSphereRadius;
497519
const shadowTexelInTileCoords1 = texelScale * this._cascades[this._cascades.length - 1].boundingSphereRadius;
498520
// Instanced model tiles could have smoothened (shared among neighbor faces) normals. Normal is not surface normal
@@ -523,7 +545,7 @@ export class ShadowRenderer {
523545
const uniforms = this._uniformValues;
524546

525547
const lightMatrix = new Float64Array(16);
526-
for (let i = 0; i < shadowParameters.cascadeCount; i++) {
548+
for (let i = 0; i < this._shadowParameters.cascadeCount; i++) {
527549
mat4.multiply(lightMatrix, this._cascades[i].matrix, worldMatrix);
528550
uniforms[i === 0 ? 'u_light_matrix_0' : 'u_light_matrix_1'] = Float32Array.from(lightMatrix);
529551
context.activeTexture.set(gl.TEXTURE0 + TextureSlots.ShadowMap0 + i);
@@ -533,7 +555,7 @@ export class ShadowRenderer {
533555
this.useNormalOffset = normalOffset;
534556

535557
if (normalOffset) {
536-
const scale = shadowParameters.normalOffset;
558+
const scale = this._shadowParameters.normalOffset;
537559
uniforms["u_shadow_normal_offset"] = [1.0, scale, scale]; // meterToTile isn't used
538560
uniforms["u_shadow_bias"] = [0.00006, 0.0012, 0.012]; // Reduce constant offset
539561
} else {

eslint.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {globalIgnores} from 'eslint/config';
99
import {includeIgnoreFile} from '@eslint/compat';
1010
import tsConfig from './tsconfig.json' with {type: 'json'};
1111
import noObjectMethodsOnCollections from './test/eslint-rules/no-object-methods-on-collections.ts';
12+
import devtoolsMustUseDebugRun from './test/eslint-rules/devtools-must-use-debug-run.ts';
1213

1314
const __filename = fileURLToPath(import.meta.url);
1415
const __dirname = path.dirname(__filename);
@@ -157,6 +158,7 @@ export default tseslint.config(
157158
mapbox: {
158159
rules: {
159160
'no-object-methods-on-collections': noObjectMethodsOnCollections,
161+
'devtools-must-use-debug-run': devtoolsMustUseDebugRun,
160162
},
161163
},
162164
},
@@ -165,6 +167,15 @@ export default tseslint.config(
165167
},
166168
},
167169

170+
// Require _devtools access inside Debug.run()
171+
{
172+
files: ['src/**/*.ts', '3d-style/**/*.ts'],
173+
ignores: ['src/ui/control/devtools.ts'],
174+
rules: {
175+
'mapbox/devtools-must-use-debug-run': 'error',
176+
},
177+
},
178+
168179
// Import plugin rules
169180
{
170181
rules: {

0 commit comments

Comments
 (0)