Skip to content

Commit

Permalink
CARTO: Fix seams between tiles in RasterTileLayer (#9286)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpalmer authored Dec 11, 2024
1 parent d1770db commit 0a4a25a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 13 deletions.
31 changes: 30 additions & 1 deletion modules/carto/src/layers/post-process-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// Copyright (c) vis.gl contributors

import {Framebuffer, TextureProps} from '@luma.gl/core';
import type {ShaderPass} from '@luma.gl/shadertools';
import {
_ConstructorOf,
CompositeLayer,
Layer,
LayerContext,
Expand Down Expand Up @@ -63,8 +65,10 @@ class DrawCallbackLayer extends Layer {
* Resulting layer must be used as a sublayer of a layer created
* with `PostProcessModifier`
*/
export function RTTModifier(BaseLayer) {
export function RTTModifier<T extends _ConstructorOf<Layer>>(BaseLayer: T): T {
// @ts-expect-error initializeState is abstract
return class RTTLayer extends BaseLayer {
// @ts-expect-error typescript doesn't see static property
static layerName = `RTT-${BaseLayer.layerName}`;

draw(this: RTTLayer, opts: any) {
Expand Down Expand Up @@ -181,5 +185,30 @@ export function PostProcessModifier<T extends Constructor<DrawableCompositeLayer

this.internalState.renderInProgress = false;
}

_finalize(): void {
this.internalState.renderBuffers.forEach((fbo: Framebuffer) => {
fbo.destroy();
});
this.internalState.renderBuffers = null;
this.internalState.postProcess.cleanup();
}
};
}

const fs = /* glsl */ `\
vec4 copy_filterColor_ext(vec4 color, vec2 texSize, vec2 texCoord) {
return color;
}
`;

/**
* Copy
* Simple module that just copies input color to output
*/
export const copy = {
name: 'copy',
fs,
getUniforms: () => ({}),
passes: [{filter: true}]
} as const satisfies ShaderPass<{}>;
18 changes: 10 additions & 8 deletions modules/carto/src/layers/raster-layer-vertex.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ out vec4 position_commonspace;
void main(void) {
// Rather than positioning using attribute, layout pixel grid using gl_InstanceID
vec2 common_position = column.offset.xy;
vec2 tileOrigin = column.offset.xy;
float scale = column.widthScale; // Re-use widthScale prop to pass cell scale
int yIndex = - (gl_InstanceID / BLOCK_WIDTH);
int xIndex = gl_InstanceID + (yIndex * BLOCK_WIDTH);
common_position += scale * vec2(float(xIndex), float(yIndex - 1));
// Avoid precision issues by applying 0.5 offset here, rather than when laying out vertices
vec2 cellCenter = scale * vec2(float(xIndex) + 0.5, float(yIndex) - 0.5);
vec4 color = column.isStroke ? instanceLineColors : instanceFillColors;
Expand All @@ -39,7 +41,7 @@ void main(void) {
// Get position directly from quadbin, rather than projecting
// Important to set geometry.position before using project_ methods below
// as geometry.worldPosition is not set (we don't know our lat/long)
geometry.position = vec4(common_position, 0.0, 1.0);
geometry.position = vec4(tileOrigin + cellCenter, 0.0, 1.0);
if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) {
geometry.position.xyz -= project.commonOrigin;
}
Expand All @@ -63,12 +65,12 @@ void main(void) {
geometry.pickingColor = instancePickingColors;
// project center of column
vec2 offset = (vec2(0.5) + positions.xy * strokeOffsetRatio) * cellWidth * shouldRender;
vec3 pos = vec3(offset, project_size(elevation));
DECKGL_FILTER_SIZE(pos, geometry);
// Cell coordinates centered on origin
vec2 base = positions.xy * scale * strokeOffsetRatio * column.coverage * shouldRender;
vec3 cell = vec3(base, project_size(elevation));
DECKGL_FILTER_SIZE(cell, geometry);
geometry.position.xyz += pos;
geometry.position.xyz += cell;
gl_Position = project_common_position_to_clipspace(geometry.position);
geometry.normal = project_normal(normals);
Expand Down
17 changes: 15 additions & 2 deletions modules/carto/src/layers/raster-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {quadbinToOffset} from './quadbin-utils';
import {Raster} from './schema/carto-raster-tile-loader';
import vs from './raster-layer-vertex.glsl';
import {createBinaryProxy} from '../utils';
import {RTTModifier} from './post-process-utils';

const defaultProps: DefaultProps<RasterLayerProps> = {
...ColumnLayer.defaultProps,
Expand All @@ -30,7 +31,8 @@ const defaultProps: DefaultProps<RasterLayerProps> = {
};

// Modified ColumnLayer with custom vertex shader
class RasterColumnLayer extends ColumnLayer {
// Use RTT to avoid inter-tile seams
class RasterColumnLayer extends RTTModifier(ColumnLayer) {
static layerName = 'RasterColumnLayer';

getShaders() {
Expand Down Expand Up @@ -143,7 +145,18 @@ export default class RasterLayer<DataT = any, ExtraProps = {}> extends Composite
offset,
lineWidthScale, // Re-use widthScale prop to pass cell scale,
highlightedObjectIndex,
highlightColor
highlightColor,

// RTT requires blending otherwise opacity < 1 blends with black
// render target
parameters: {
blendColorSrcFactor: 'one',
blendAlphaSrcFactor: 'one',
blendColorDstFactor: 'zero',
blendAlphaDstFactor: 'zero',
blendColorOperation: 'add',
blendAlphaOperation: 'add'
}
}
);
}
Expand Down
23 changes: 21 additions & 2 deletions modules/carto/src/layers/raster-tile-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {CompositeLayer, CompositeLayerProps, DefaultProps, Layer, LayersList} from '@deck.gl/core';
import {
CompositeLayer,
CompositeLayerProps,
DefaultProps,
FilterContext,
Layer,
LayersList
} from '@deck.gl/core';
import RasterLayer, {RasterLayerProps} from './raster-layer';
import QuadbinTileset2D from './quadbin-tileset-2d';
import type {TilejsonResult} from '@carto/api-client';
import {injectAccessToken, TilejsonPropType} from './utils';
import {DEFAULT_TILE_SIZE} from '../constants';
import {TileLayer, TileLayerProps} from '@deck.gl/geo-layers';
import {copy, PostProcessModifier} from './post-process-utils';

export const renderSubLayers = props => {
const tileIndex = props.tile?.index?.q;
Expand All @@ -18,6 +26,7 @@ export const renderSubLayers = props => {

const defaultProps: DefaultProps<RasterTileLayerProps> = {
data: TilejsonPropType,
refinementStrategy: 'no-overlap',
tileSize: DEFAULT_TILE_SIZE
};

Expand All @@ -31,6 +40,16 @@ type _RasterTileLayerProps<DataT> = Omit<RasterLayerProps<DataT>, 'data'> &
data: null | TilejsonResult | Promise<TilejsonResult>;
};

class PostProcessTileLayer extends PostProcessModifier(TileLayer, copy) {
filterSubLayer(context: FilterContext) {
// Handle DrawCallbackLayer
const {tile} = (context.layer as Layer<{tile: any}>).props;
if (!tile) return true;

return super.filterSubLayer(context);
}
}

export default class RasterTileLayer<
DataT = any,
ExtraProps extends {} = {}
Expand All @@ -50,7 +69,7 @@ export default class RasterTileLayer<
if (!tileJSON) return null;

const {tiles: data, minzoom: minZoom, maxzoom: maxZoom} = tileJSON;
const SubLayerClass = this.getSubLayerClass('tile', TileLayer);
const SubLayerClass = this.getSubLayerClass('tile', PostProcessTileLayer);
return new SubLayerClass(this.props, {
id: `raster-tile-layer-${this.props.id}`,
data,
Expand Down

0 comments on commit 0a4a25a

Please sign in to comment.