From f3487637a3edb87ca9c81a3766f96d3a589172d4 Mon Sep 17 00:00:00 2001 From: Joseph Beuckman Date: Fri, 8 Apr 2022 12:41:40 -0500 Subject: [PATCH 1/4] debug demo page --- debug/query-raster.html | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 debug/query-raster.html diff --git a/debug/query-raster.html b/debug/query-raster.html new file mode 100644 index 00000000000..0c8d8de5c7b --- /dev/null +++ b/debug/query-raster.html @@ -0,0 +1,66 @@ + + + + Mapbox GL JS debug page + + + + + + + +
+
+ + + + + + From a80a3a7000970355a5312807e7ff9661f9891460 Mon Sep 17 00:00:00 2001 From: Joseph Beuckman Date: Fri, 8 Apr 2022 12:48:28 -0500 Subject: [PATCH 2/4] version of render to get pixel color --- src/render/painter.js | 287 ++++++++++++++++++++++++++++++++++++++++++ src/ui/map.js | 4 + 2 files changed, 291 insertions(+) diff --git a/src/render/painter.js b/src/render/painter.js index 3cc4f178995..22647d944f0 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -718,6 +718,293 @@ class Painter { } } + colorForLayerPoint(layerId: string, point: PointLike) { + // this.style = style; + // this.options = options; + + // this.lineAtlas = style.lineAtlas; + // this.imageManager = style.imageManager; + // this.glyphManager = style.glyphManager; + // + // this.symbolFadeChange = style.placement.symbolFadeChange(browser.now()); + // + this.imageManager.beginFrame(); + + const layer = this.style._layers[layerId]; + const sourceCache = this.style._getLayerSourceCache(layer); + + // const layerIds = this.style.order; + const layerIds = [layerId]; + // const sourceCaches = this.style._sourceCaches; + const sourceCaches = { [sourceCache.id]: sourceCache }; + + // for (const id in sourceCaches) { + // const sourceCache = sourceCaches[id]; + // if (sourceCache.used) { + sourceCache.prepare(this.context); + // } + // } + + const coordsAscending: { [_: string]: Array } = {}; + const coordsDescending: { [_: string]: Array } = {}; + const coordsDescendingSymbol: { + [_: string]: Array, + } = {}; + + for (const id in sourceCaches) { + const sourceCache = sourceCaches[id]; + coordsAscending[id] = sourceCache.getVisibleCoordinates(); + coordsDescending[id] = coordsAscending[id].slice().reverse(); + coordsDescendingSymbol[id] = sourceCache + .getVisibleCoordinates(true) + .reverse(); + } + + this.opaquePassCutoff = Infinity; + for (let i = 0; i < layerIds.length; i++) { + const layerId = layerIds[i]; + if (this.style._layers[layerId].is3D()) { + this.opaquePassCutoff = i; + break; + } + } + + // if (this.terrain) { + // this.terrain.updateTileBinding(coordsDescendingSymbol); + // // All render to texture is done in translucent pass to remove need + // // for depth buffer allocation per tile. + // this.opaquePassCutoff = 0; + // } + + if ( + this.transform.projection.name === "globe" && + !this.globeSharedBuffers + ) { + this.globeSharedBuffers = new GlobeSharedBuffers(this.context); + } + + // Following line is billing related code. Do not change. See LICENSE.txt + if (!isMapAuthenticated(this.context.gl)) return; + + // Offscreen pass =============================================== + // We first do all rendering that requires rendering to a separate + // framebuffer, and then save those for rendering back to the map + // later: in doing this we avoid doing expensive framebuffer restores. + this.renderPass = "offscreen"; + + // for (const layerId of layerIds) { + // const layer = this.style._layers[layerId]; + // const sourceCache = this.style._getLayerSourceCache(layer); + if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom)) { + } else { + const coords = sourceCache + ? coordsDescending[sourceCache.id] + : undefined; + if ( + !(layer.type === "custom" || layer.isSky()) && + !(coords && coords.length) + ) { + } else { + this.renderLayer(this, sourceCache, layer, coords); + } + } + + this.depthRangeFor3D = [ + 0, + 1 - + (this.style.order.length + 2) * + this.numSublayers * + this.depthEpsilon, + ]; + + // Terrain depth offscreen render pass ========================== + // With terrain on, renders the depth buffer into a texture. + // This texture is used for occlusion testing (labels) + if ( + this.terrain && + (this.style.hasSymbolLayers() || this.style.hasCircleLayers()) + ) { + this.terrain.drawDepth(); + } + + // Rebind the main framebuffer now that all offscreen layers have been rendered: + this.context.bindFramebuffer.set(null); + this.context.viewport.set([0, 0, this.width, this.height]); + + // Clear buffers in preparation for drawing to the main framebuffer + // If fog is enabled, use the fog color as default clear color. + let clearColor = Color.transparent; + if (this.style.fog && this.style.fog.getOpacity(this.transform.pitch)) { + clearColor = this.style.fog.properties.get("color"); + } + this.context.clear({ + color: Color.black, //options.showOverdrawInspector ? Color.black : clearColor, + depth: 1, + }); + this.clearStencil(); + + this._showOverdrawInspector = false; //options.showOverdrawInspector; + + // Opaque pass =============================================== + // Draw opaque layers top-to-bottom first. + this.renderPass = "opaque"; + + if (!this.terrain) { + for ( + this.currentLayer = layerIds.length - 1; + this.currentLayer >= 0; + this.currentLayer-- + ) { + const layer = this.style._layers[layerIds[this.currentLayer]]; + const sourceCache = this.style._getLayerSourceCache(layer); + if (layer.isSky()) continue; + const coords = sourceCache + ? coordsDescending[sourceCache.id] + : undefined; + + this._renderTileClippingMasks(layer, sourceCache, coords); + this.renderLayer(this, sourceCache, layer, coords); + } + } + + // Sky pass ====================================================== + // Draw all sky layers bottom to top. + // They are drawn at max depth, they are drawn after opaque and before + // translucent to fail depth testing and mix with translucent objects. + this.renderPass = "sky"; + const isTransitioning = + globeToMercatorTransition(this.transform.zoom) > 0.0; + if ( + (isTransitioning || this.transform.projection.name !== "globe") && + this.transform.isHorizonVisible() + ) { + for ( + this.currentLayer = 0; + this.currentLayer < layerIds.length; + this.currentLayer++ + ) { + const layer = this.style._layers[layerIds[this.currentLayer]]; + const sourceCache = this.style._getLayerSourceCache(layer); + if (!layer.isSky()) continue; + const coords = sourceCache + ? coordsDescending[sourceCache.id] + : undefined; + + this.renderLayer(this, sourceCache, layer, coords); + } + } + if (this.transform.projection.name === "globe") { + drawGlobeAtmosphere(this); + } + + // Translucent pass =============================================== + // Draw all other layers bottom-to-top. + this.renderPass = "translucent"; + + this.currentLayer = 0; + while (this.currentLayer < 1) { + // const layer = this.style._layers[layerIds[this.currentLayer]]; + // const sourceCache = this.style._getLayerSourceCache(layer); + + // Nothing to draw in translucent pass for sky layers, advance + if (layer.isSky()) { + ++this.currentLayer; + continue; + } + + // With terrain on and for draped layers only, issue rendering and progress + // this.currentLayer until the next non-draped layer. + // Otherwise we interleave terrain draped render with non-draped layers on top + if (this.terrain && this.style.isLayerDraped(layer)) { + if (layer.isHidden(this.transform.zoom)) { + ++this.currentLayer; + continue; + } + const terrain = ((this.terrain: any): Terrain); + const prevLayer = this.currentLayer; + this.currentLayer = terrain.renderBatch(this.currentLayer); + assert(this.context.bindFramebuffer.current === null); + assert(this.currentLayer > prevLayer); + continue; + } + + // For symbol layers in the translucent pass, we add extra tiles to the renderable set + // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render + // separate clipping masks + const coords = sourceCache + ? (layer.type === "symbol" + ? coordsDescendingSymbol + : coordsDescending)[sourceCache.id] + : undefined; + + this._renderTileClippingMasks( + layer, + sourceCache, + sourceCache ? coordsAscending[sourceCache.id] : undefined + ); + this.renderLayer(this, sourceCache, layer, coords); + + ++this.currentLayer; + } + + if (this.terrain) { + this.terrain.postRender(); + } + + // if (this.options.showTileBoundaries || this.options.showQueryGeometry) { + // //Use source with highest maxzoom + // let selectedSource = null; + // const layers = values(this.style._layers); + // layers.forEach((layer) => { + // const sourceCache = style._getLayerSourceCache(layer); + // if (sourceCache && !layer.isHidden(this.transform.zoom)) { + // if (!selectedSource || (selectedSource.getSource().maxzoom < sourceCache.getSource().maxzoom)) { + // selectedSource = sourceCache; + // } + // } + // }); + // if (selectedSource) { + // if (this.options.showTileBoundaries) { + // draw.debug(this, selectedSource, selectedSource.getVisibleCoordinates()); + // } + // + // Debug.run(() => { + // if (this.options.showQueryGeometry && selectedSource) { + // drawDebugQueryGeometry(this, selectedSource, selectedSource.getVisibleCoordinates()); + // } + // }); + // } + // } + // + // if (this.options.showPadding) { + // drawDebugPadding(this); + // } + + // Set defaults for most GL values so that anyone using the state after the render + // encounters more expected values. + // this.context.setDefault(); + // this.frameCounter = (this.frameCounter + 1) % Number.MAX_SAFE_INTEGER; + // + // if (this.tileLoaded && this.options.speedIndexTiming) { + // this.loadTimeStamps.push(window.performance.now()); + // this.saveCanvasCopy(); + // } + + const gl = this.context.gl; + var pixel = new Uint8Array(4); + gl.readPixels( + point.x, + this.height - point.y, + 1, + 1, + gl.RGBA, + gl.UNSIGNED_BYTE, + pixel + ); + + return pixel; + } + renderLayer(painter: Painter, sourceCache?: SourceCache, layer: StyleLayer, coords?: Array) { if (layer.isHidden(this.transform.zoom)) return; if (layer.type !== 'background' && layer.type !== 'sky' && layer.type !== 'custom' && !(coords && coords.length)) return; diff --git a/src/ui/map.js b/src/ui/map.js index 9af38147d47..e49c86595b4 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -1504,6 +1504,10 @@ class Map extends Camera { /** @section {Querying features} */ + colorForLayerPoint(layerId: string, point: PointLike) { + return this.painter.colorForLayerPoint(layerId, point) + } + /** * Returns an array of [GeoJSON](http://geojson.org/) * [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2) From ee09de4bb86976664226f98e9575b8cdf412a9b2 Mon Sep 17 00:00:00 2001 From: Joseph Beuckman Date: Sat, 9 Apr 2022 20:37:22 -0500 Subject: [PATCH 3/4] simplify render function --- debug/query-raster.html | 7 +- src/render/painter.js | 264 +++------------------------------------- 2 files changed, 20 insertions(+), 251 deletions(-) diff --git a/debug/query-raster.html b/debug/query-raster.html index 0c8d8de5c7b..6b6354161db 100644 --- a/debug/query-raster.html +++ b/debug/query-raster.html @@ -42,13 +42,8 @@ style: "mapbox://styles/mapbox/satellite-v9", hash: true, })); + map.on("load", function () { - map.addSource("mapbox-dem", { - type: "raster-dem", - url: "mapbox://mapbox.terrain-rgb", - tileSize: 512, - maxzoom: 14, - }); map.on("mousemove", (event) => { const pixel = map.colorForLayerPoint( diff --git a/src/render/painter.js b/src/render/painter.js index 22647d944f0..9498476d7db 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -719,276 +719,50 @@ class Painter { } colorForLayerPoint(layerId: string, point: PointLike) { - // this.style = style; - // this.options = options; - - // this.lineAtlas = style.lineAtlas; - // this.imageManager = style.imageManager; - // this.glyphManager = style.glyphManager; - // - // this.symbolFadeChange = style.placement.symbolFadeChange(browser.now()); - // - this.imageManager.beginFrame(); const layer = this.style._layers[layerId]; const sourceCache = this.style._getLayerSourceCache(layer); - // const layerIds = this.style.order; const layerIds = [layerId]; - // const sourceCaches = this.style._sourceCaches; - const sourceCaches = { [sourceCache.id]: sourceCache }; - // for (const id in sourceCaches) { - // const sourceCache = sourceCaches[id]; - // if (sourceCache.used) { sourceCache.prepare(this.context); - // } - // } - - const coordsAscending: { [_: string]: Array } = {}; - const coordsDescending: { [_: string]: Array } = {}; - const coordsDescendingSymbol: { - [_: string]: Array, - } = {}; - for (const id in sourceCaches) { - const sourceCache = sourceCaches[id]; - coordsAscending[id] = sourceCache.getVisibleCoordinates(); - coordsDescending[id] = coordsAscending[id].slice().reverse(); - coordsDescendingSymbol[id] = sourceCache + const coordsAscending: Array = sourceCache.getVisibleCoordinates(); + const coordsDescending: Array = coordsAscending.slice().reverse(); + const coordsDescendingSymbol: Array = sourceCache .getVisibleCoordinates(true) .reverse(); - } - - this.opaquePassCutoff = Infinity; - for (let i = 0; i < layerIds.length; i++) { - const layerId = layerIds[i]; - if (this.style._layers[layerId].is3D()) { - this.opaquePassCutoff = i; - break; - } - } - - // if (this.terrain) { - // this.terrain.updateTileBinding(coordsDescendingSymbol); - // // All render to texture is done in translucent pass to remove need - // // for depth buffer allocation per tile. - // this.opaquePassCutoff = 0; - // } - - if ( - this.transform.projection.name === "globe" && - !this.globeSharedBuffers - ) { - this.globeSharedBuffers = new GlobeSharedBuffers(this.context); - } - - // Following line is billing related code. Do not change. See LICENSE.txt - if (!isMapAuthenticated(this.context.gl)) return; - - // Offscreen pass =============================================== - // We first do all rendering that requires rendering to a separate - // framebuffer, and then save those for rendering back to the map - // later: in doing this we avoid doing expensive framebuffer restores. - this.renderPass = "offscreen"; - - // for (const layerId of layerIds) { - // const layer = this.style._layers[layerId]; - // const sourceCache = this.style._getLayerSourceCache(layer); - if (!layer.hasOffscreenPass() || layer.isHidden(this.transform.zoom)) { - } else { - const coords = sourceCache - ? coordsDescending[sourceCache.id] - : undefined; - if ( - !(layer.type === "custom" || layer.isSky()) && - !(coords && coords.length) - ) { - } else { - this.renderLayer(this, sourceCache, layer, coords); - } - } - - this.depthRangeFor3D = [ - 0, - 1 - - (this.style.order.length + 2) * - this.numSublayers * - this.depthEpsilon, - ]; - - // Terrain depth offscreen render pass ========================== - // With terrain on, renders the depth buffer into a texture. - // This texture is used for occlusion testing (labels) - if ( - this.terrain && - (this.style.hasSymbolLayers() || this.style.hasCircleLayers()) - ) { - this.terrain.drawDepth(); - } // Rebind the main framebuffer now that all offscreen layers have been rendered: - this.context.bindFramebuffer.set(null); + // this.context.bindFramebuffer.set(null); this.context.viewport.set([0, 0, this.width, this.height]); - // Clear buffers in preparation for drawing to the main framebuffer - // If fog is enabled, use the fog color as default clear color. - let clearColor = Color.transparent; - if (this.style.fog && this.style.fog.getOpacity(this.transform.pitch)) { - clearColor = this.style.fog.properties.get("color"); - } this.context.clear({ - color: Color.black, //options.showOverdrawInspector ? Color.black : clearColor, + color: 'rgba(0,0,0,0)', //options.showOverdrawInspector ? Color.black : clearColor, depth: 1, }); this.clearStencil(); this._showOverdrawInspector = false; //options.showOverdrawInspector; - // Opaque pass =============================================== - // Draw opaque layers top-to-bottom first. - this.renderPass = "opaque"; - - if (!this.terrain) { - for ( - this.currentLayer = layerIds.length - 1; - this.currentLayer >= 0; - this.currentLayer-- - ) { - const layer = this.style._layers[layerIds[this.currentLayer]]; - const sourceCache = this.style._getLayerSourceCache(layer); - if (layer.isSky()) continue; - const coords = sourceCache - ? coordsDescending[sourceCache.id] - : undefined; - - this._renderTileClippingMasks(layer, sourceCache, coords); - this.renderLayer(this, sourceCache, layer, coords); - } - } - - // Sky pass ====================================================== - // Draw all sky layers bottom to top. - // They are drawn at max depth, they are drawn after opaque and before - // translucent to fail depth testing and mix with translucent objects. - this.renderPass = "sky"; - const isTransitioning = - globeToMercatorTransition(this.transform.zoom) > 0.0; - if ( - (isTransitioning || this.transform.projection.name !== "globe") && - this.transform.isHorizonVisible() - ) { - for ( - this.currentLayer = 0; - this.currentLayer < layerIds.length; - this.currentLayer++ - ) { - const layer = this.style._layers[layerIds[this.currentLayer]]; - const sourceCache = this.style._getLayerSourceCache(layer); - if (!layer.isSky()) continue; - const coords = sourceCache - ? coordsDescending[sourceCache.id] - : undefined; - - this.renderLayer(this, sourceCache, layer, coords); - } - } - if (this.transform.projection.name === "globe") { - drawGlobeAtmosphere(this); - } - - // Translucent pass =============================================== - // Draw all other layers bottom-to-top. this.renderPass = "translucent"; - this.currentLayer = 0; - while (this.currentLayer < 1) { - // const layer = this.style._layers[layerIds[this.currentLayer]]; - // const sourceCache = this.style._getLayerSourceCache(layer); - // Nothing to draw in translucent pass for sky layers, advance - if (layer.isSky()) { - ++this.currentLayer; - continue; - } + // For symbol layers in the translucent pass, we add extra tiles to the renderable set + // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render + // separate clipping masks + const coords = sourceCache + ? (layer.type === "symbol" + ? coordsDescendingSymbol + : coordsDescending) + : undefined; - // With terrain on and for draped layers only, issue rendering and progress - // this.currentLayer until the next non-draped layer. - // Otherwise we interleave terrain draped render with non-draped layers on top - if (this.terrain && this.style.isLayerDraped(layer)) { - if (layer.isHidden(this.transform.zoom)) { - ++this.currentLayer; - continue; - } - const terrain = ((this.terrain: any): Terrain); - const prevLayer = this.currentLayer; - this.currentLayer = terrain.renderBatch(this.currentLayer); - assert(this.context.bindFramebuffer.current === null); - assert(this.currentLayer > prevLayer); - continue; - } - - // For symbol layers in the translucent pass, we add extra tiles to the renderable set - // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render - // separate clipping masks - const coords = sourceCache - ? (layer.type === "symbol" - ? coordsDescendingSymbol - : coordsDescending)[sourceCache.id] - : undefined; - - this._renderTileClippingMasks( - layer, - sourceCache, - sourceCache ? coordsAscending[sourceCache.id] : undefined - ); - this.renderLayer(this, sourceCache, layer, coords); - - ++this.currentLayer; - } - - if (this.terrain) { - this.terrain.postRender(); - } - - // if (this.options.showTileBoundaries || this.options.showQueryGeometry) { - // //Use source with highest maxzoom - // let selectedSource = null; - // const layers = values(this.style._layers); - // layers.forEach((layer) => { - // const sourceCache = style._getLayerSourceCache(layer); - // if (sourceCache && !layer.isHidden(this.transform.zoom)) { - // if (!selectedSource || (selectedSource.getSource().maxzoom < sourceCache.getSource().maxzoom)) { - // selectedSource = sourceCache; - // } - // } - // }); - // if (selectedSource) { - // if (this.options.showTileBoundaries) { - // draw.debug(this, selectedSource, selectedSource.getVisibleCoordinates()); - // } - // - // Debug.run(() => { - // if (this.options.showQueryGeometry && selectedSource) { - // drawDebugQueryGeometry(this, selectedSource, selectedSource.getVisibleCoordinates()); - // } - // }); - // } - // } - // - // if (this.options.showPadding) { - // drawDebugPadding(this); - // } - - // Set defaults for most GL values so that anyone using the state after the render - // encounters more expected values. - // this.context.setDefault(); - // this.frameCounter = (this.frameCounter + 1) % Number.MAX_SAFE_INTEGER; - // - // if (this.tileLoaded && this.options.speedIndexTiming) { - // this.loadTimeStamps.push(window.performance.now()); - // this.saveCanvasCopy(); - // } + this._renderTileClippingMasks( + layer, + sourceCache, + sourceCache ? coordsAscending : undefined + ); + this.renderLayer(this, sourceCache, layer, coords); const gl = this.context.gl; var pixel = new Uint8Array(4); From c71b1047282032e0fa38c2702c97d862223124b9 Mon Sep 17 00:00:00 2001 From: Joe Beuckman Date: Mon, 25 Jul 2022 21:11:56 -0500 Subject: [PATCH 4/4] pixel ratio --- src/render/painter.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/render/painter.js b/src/render/painter.js index 9498476d7db..41d9751d180 100644 --- a/src/render/painter.js +++ b/src/render/painter.js @@ -721,17 +721,14 @@ class Painter { colorForLayerPoint(layerId: string, point: PointLike) { const layer = this.style._layers[layerId]; - const sourceCache = this.style._getLayerSourceCache(layer); + if (layer.type !== 'raster') { return } - const layerIds = [layerId]; + const sourceCache = this.style._getLayerSourceCache(layer); sourceCache.prepare(this.context); const coordsAscending: Array = sourceCache.getVisibleCoordinates(); const coordsDescending: Array = coordsAscending.slice().reverse(); - const coordsDescendingSymbol: Array = sourceCache - .getVisibleCoordinates(true) - .reverse(); // Rebind the main framebuffer now that all offscreen layers have been rendered: // this.context.bindFramebuffer.set(null); @@ -747,14 +744,11 @@ class Painter { this.renderPass = "translucent"; - // For symbol layers in the translucent pass, we add extra tiles to the renderable set // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render // separate clipping masks const coords = sourceCache - ? (layer.type === "symbol" - ? coordsDescendingSymbol - : coordsDescending) + ? coordsDescending : undefined; this._renderTileClippingMasks( @@ -767,8 +761,8 @@ class Painter { const gl = this.context.gl; var pixel = new Uint8Array(4); gl.readPixels( - point.x, - this.height - point.y, + point.x * window.devicePixelRatio, + this.height - point.y * window.devicePixelRatio, 1, 1, gl.RGBA,