Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(layers): ScatterplotLayer uniform buffer #8132

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a3999a2
feat: Remove webgl-legacy
ibgreen Dec 1, 2022
6b5ce27
wip
ibgreen Aug 7, 2023
e1fffaa
wip
ibgreen Aug 7, 2023
87f578f
wip
ibgreen Aug 7, 2023
8f9e018
wip
ibgreen Aug 8, 2023
4337c8e
wip
ibgreen Aug 8, 2023
df5da65
alpha.26
ibgreen Aug 8, 2023
46c460f
constant attribute fixes
ibgreen Aug 12, 2023
ecbd941
add bufferMapping
ibgreen Aug 16, 2023
baf4360
wip
Pessimistress Aug 8, 2023
49db389
wip
Pessimistress Aug 19, 2023
e1691bd
alpha.30. api->core. boolean uniform fix
ibgreen Aug 20, 2023
d73400a
restore ScatterplotLayer picking
Pessimistress Aug 20, 2023
7c7729b
Fix Deck class canvas creation
Pessimistress Aug 20, 2023
1cfa025
layer browser fixes for testing
Pessimistress Aug 20, 2023
61faac7
alpha.31
ibgreen Aug 23, 2023
f8082a1
bufferLayout
Pessimistress Aug 23, 2023
3436bdd
Fix IconLayer
ibgreen Aug 23, 2023
059146e
Fix TextLayer and BitmapLayer
Pessimistress Aug 23, 2023
ebfcba4
alpha.32
ibgreen Aug 23, 2023
73ce5d2
wip
ibgreen Aug 23, 2023
f5119a6
wip
ibgreen Aug 24, 2023
5f5d233
Fix ColumnLayer and SolidPolygonLayer
Pessimistress Aug 24, 2023
3b20145
Fix bitmaplayer
Pessimistress Aug 26, 2023
43dbd0d
updated bufferlayouts
ibgreen Aug 26, 2023
ac2def1
small partial fixes
ibgreen Aug 27, 2023
2f0bd7e
lint
ibgreen Sep 23, 2023
5eb7ed8
alpha.36
ibgreen Sep 23, 2023
90efbc4
prettier
ibgreen Sep 25, 2023
edaf8fa
cleanup
ibgreen Sep 25, 2023
3bfeadc
feat(layers): ScatterplpotLayer uniform buffer
ibgreen Sep 23, 2023
8fc83f6
Merge branch 'master' into ib/scatterplot-uniform-buffer
ibgreen Sep 26, 2023
359d955
fix
ibgreen Sep 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/layers/src/bitmap-layer/bitmap-layer.ts
Expand Up @@ -33,7 +33,7 @@ import {
Position,
DefaultProps
} from '@deck.gl/core';
import {Geometry, Model} from '@luma.gl/engine';
import {Model} from '@luma.gl/engine';
import type {Texture} from '@luma.gl/core';
import {GL} from '@luma.gl/constants';
import {lngLatToWorld} from '@math.gl/web-mercator';
Expand Down
Expand Up @@ -18,47 +18,66 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

export default `\
import {glsl} from '@luma.gl/core';

export default glsl`\
#version 300 es
#define SHADER_NAME scatterplot-layer-fragment-shader

precision highp float;

uniform bool filled;
uniform float stroked;
uniform bool antialiasing;
precision highp int;

varying vec4 vFillColor;
varying vec4 vLineColor;
varying vec2 unitPosition;
varying float innerUnitRadius;
varying float outerRadiusPixels;

// Needs to be identical to vertex shader uniforms
uniform scatterplotUniforms {
// float opacity;
float radiusScale;
float radiusMinPixels;
float radiusMaxPixels;
float lineWidthScale;
float lineWidthMinPixels;
float lineWidthMaxPixels;
float stroked;
bool filled;
bool antialiasing;
bool billboard;
int radiusUnits;
int lineWidthUnits;
} scatterplot;

uniform float opacity;

void main(void) {
geometry.uv = unitPosition;

float distToCenter = length(unitPosition) * outerRadiusPixels;
float inCircle = antialiasing ?
float inCircle = scatterplot.antialiasing ?
smoothedge(distToCenter, outerRadiusPixels) :
step(distToCenter, outerRadiusPixels);

if (inCircle == 0.0) {
discard;
}

if (stroked > 0.5) {
float isLine = antialiasing ?
if (scatterplot.stroked > 0.5) {
float isLine = scatterplot.antialiasing ?
smoothedge(innerUnitRadius * outerRadiusPixels, distToCenter) :
step(innerUnitRadius * outerRadiusPixels, distToCenter);

if (filled) {
if (scatterplot.filled) {
gl_FragColor = mix(vFillColor, vLineColor, isLine);
} else {
if (isLine == 0.0) {
discard;
}
gl_FragColor = vec4(vLineColor.rgb, vLineColor.a * isLine);
}
} else if (!filled) {
} else if (!scatterplot.filled) {
discard;
} else {
gl_FragColor = vFillColor;
Expand Down
Expand Up @@ -18,7 +18,46 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

export default `\
import {ShaderUniformType, glsl} from '@luma.gl/core';

export type ScatterplotLayerUniforms = {
// opacity: number;
radiusScale: number;
radiusMinPixels: number;
radiusMaxPixels: number;
lineWidthScale: number;
lineWidthMinPixels: number;
lineWidthMaxPixels: number;
stroked: number;
filled: boolean;
antialiasing: boolean;
billboard: boolean;
radiusUnits: number;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More restrictive type? 0 | 1 | 2

lineWidthUnits: number;
};

export const scatterplot: {uniformTypes: Record<string, ShaderUniformType>} = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little confusing to see this constant referenced in the layer code. Maybe name it ScatterplotUniformLayout?

uniformTypes: {
// opacity: 'f32',
radiusScale: 'f32',
radiusMinPixels: 'f32',
radiusMaxPixels: 'f32',
lineWidthScale: 'f32',
lineWidthMinPixels: 'f32',
lineWidthMaxPixels: 'f32',
stroked: 'f32',
filled: 'f32',
antialiasing: 'f32',
billboard: 'f32',
radiusUnits: 'i32',
lineWidthUnits: 'i32'
}
};

const SMOOTH_EDGE_RADIUS = 0.5;

export default glsl`\
#version 300 es
#define SHADER_NAME scatterplot-layer-vertex-shader

attribute vec3 positions;
Expand All @@ -31,56 +70,60 @@ attribute vec4 instanceFillColors;
attribute vec4 instanceLineColors;
attribute vec3 instancePickingColors;

uniform float opacity;
uniform float radiusScale;
uniform float radiusMinPixels;
uniform float radiusMaxPixels;
uniform float lineWidthScale;
uniform float lineWidthMinPixels;
uniform float lineWidthMaxPixels;
uniform float stroked;
uniform bool filled;
uniform bool antialiasing;
uniform bool billboard;
uniform int radiusUnits;
uniform int lineWidthUnits;

varying vec4 vFillColor;
varying vec4 vLineColor;
varying vec2 unitPosition;
varying float innerUnitRadius;
varying float outerRadiusPixels;

// Needs to be identical to fragment shader uniforms
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it needs to be identical, could you define in one place? Derive it from scatterplot.uniformTypes?

Copy link
Collaborator Author

@ibgreen ibgreen Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, though there are some tradeoffs to it. So far it seems the shader linker complains loudly if they are different so it does not seem to be a subtle source of errors.

I tried breaking the uniforms into a separate string and injecting it. A minor problem with that is that I use the vscode GLSL syntax highlighter to make these glsl tagged strings readable, and it breaks if I add template string arguments.

I actually added code for generating uniform declarations from uniform types (in the @luma.gl/shadertools module), but I haven't started using that yet.

The high-level question for the TSC is perhaps: how much shader code should be autogenerated? Once one starts down that dark path...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all the uniforms need to be typed out in both the vertex and fragment shader? It seems unnecessary to have to include uniforms in the vertex shader that only the fragment shader reads and vice-versa

I do agree there is a tradeoff between saving time and having shaders that are actually readable in the source

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all the uniforms need to be typed out in both the vertex and fragment shader? It seems unnecessary to have to include uniforms in the vertex shader that only the fragment shader reads and vice-versa

Yes I believe so, though more experience may show otherwise. Also we need to see how this works in both GLSL and WGSL.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with you there. Auto-generation does add a layer of complexity, making it all the more important to have solid debugging tools. I'm all for it, just think we really need to keep an eye on the debugging side of things to make sure everything runs smoothly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, in the spirit of keeping it simple & landing v9 I'm for just typing out the uniforms in full at this stage

uniform scatterplotUniforms {
// float opacity;
float radiusScale;
float radiusMinPixels;
float radiusMaxPixels;
float lineWidthScale;
float lineWidthMinPixels;
float lineWidthMaxPixels;
float stroked;
bool filled;
bool antialiasing;
bool billboard;
int radiusUnits;
int lineWidthUnits;
} scatterplot;

uniform float opacity;

void main(void) {
geometry.worldPosition = instancePositions;

// Multiply out radius and clamp to limits
outerRadiusPixels = clamp(
project_size_to_pixel(radiusScale * instanceRadius, radiusUnits),
radiusMinPixels, radiusMaxPixels
project_size_to_pixel(scatterplot.radiusScale * instanceRadius, scatterplot.radiusUnits),
scatterplot.radiusMinPixels, scatterplot.radiusMaxPixels
);

// Multiply out line width and clamp to limits
float lineWidthPixels = clamp(
project_size_to_pixel(lineWidthScale * instanceLineWidths, lineWidthUnits),
lineWidthMinPixels, lineWidthMaxPixels
project_size_to_pixel(scatterplot.lineWidthScale * instanceLineWidths, scatterplot.lineWidthUnits),
scatterplot.lineWidthMinPixels, scatterplot.lineWidthMaxPixels
);

// outer radius needs to offset by half stroke width
outerRadiusPixels += stroked * lineWidthPixels / 2.0;
outerRadiusPixels += scatterplot.stroked * lineWidthPixels / 2.0;

// Expand geometry to accomodate edge smoothing
float edgePadding = antialiasing ? (outerRadiusPixels + SMOOTH_EDGE_RADIUS) / outerRadiusPixels : 1.0;
// Expand geometry to accommodate edge smoothing
float edgePadding = scatterplot.antialiasing ? (outerRadiusPixels + SMOOTH_EDGE_RADIUS) / outerRadiusPixels : 1.0;

// position on the containing square in [-1, 1] space
unitPosition = edgePadding * positions.xy;
geometry.uv = unitPosition;
geometry.pickingColor = instancePickingColors;

innerUnitRadius = 1.0 - stroked * lineWidthPixels / outerRadiusPixels;
innerUnitRadius = 1.0 - scatterplot.stroked * lineWidthPixels / outerRadiusPixels;

if (billboard) {
if (scatterplot.billboard) {
gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, vec3(0.0), geometry.position);
DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
vec3 offset = edgePadding * positions * outerRadiusPixels;
Expand Down
52 changes: 36 additions & 16 deletions modules/layers/src/scatterplot-layer/scatterplot-layer.ts
Expand Up @@ -19,13 +19,11 @@
// THE SOFTWARE.

import {Layer, project32, picking, UNIT} from '@deck.gl/core';
import {UniformStore} from '@luma.gl/core';
import {Geometry} from '@luma.gl/engine';
import {Model} from '@luma.gl/engine';
import {GL} from '@luma.gl/constants';

import vs from './scatterplot-layer-vertex.glsl';
import fs from './scatterplot-layer-fragment.glsl';

import type {
LayerProps,
LayerDataSource,
Expand All @@ -37,6 +35,9 @@ import type {
DefaultProps
} from '@deck.gl/core';

import vs, {ScatterplotLayerUniforms, scatterplot} from './scatterplot-layer-vertex.glsl';
import fs from './scatterplot-layer-fragment.glsl';

const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];

/** All props supported by the ScatterplotLayer */
Expand Down Expand Up @@ -181,11 +182,19 @@ export default class ScatterplotLayer<DataT = any, ExtraPropsT extends {} = {}>
static defaultProps = defaultProps;
static layerName: string = 'ScatterplotLayer';

state!: Layer['state'] & {
uniformStore: UniformStore<{scatterplot: ScatterplotLayerUniforms}>;
};

getShaders() {
return super.getShaders({vs, fs, modules: [project32, picking]});
}

initializeState() {
this.state.uniformStore = new UniformStore<{scatterplot: ScatterplotLayerUniforms}>({
scatterplot
});

this.getAttributeManager()!.addInstanced({
instancePositions: {
size: 3,
Expand Down Expand Up @@ -251,20 +260,25 @@ export default class ScatterplotLayer<DataT = any, ExtraPropsT extends {} = {}>
lineWidthMaxPixels
} = this.props;

// opacity etc
this.state.model.setUniforms(uniforms);
this.state.model.setUniforms({
stroked: stroked ? 1 : 0,
filled,
billboard,
antialiasing,
radiusUnits: UNIT[radiusUnits],
radiusScale,
radiusMinPixels,
radiusMaxPixels,
lineWidthUnits: UNIT[lineWidthUnits],
lineWidthScale,
lineWidthMinPixels,
lineWidthMaxPixels

// scatterplot uniform block
this.state.uniformStore.setUniforms({
scatterplot: {
stroked: stroked ? 1 : 0,
filled,
billboard,
antialiasing,
radiusUnits: UNIT[radiusUnits],
radiusScale,
radiusMinPixels,
radiusMaxPixels,
lineWidthUnits: UNIT[lineWidthUnits],
lineWidthScale,
lineWidthMinPixels,
lineWidthMaxPixels
}
});
this.state.model.draw(this.context.renderPass);
}
Expand All @@ -282,6 +296,12 @@ export default class ScatterplotLayer<DataT = any, ExtraPropsT extends {} = {}>
positions: {size: 3, value: new Float32Array(positions)}
}
}),
bindings: {
scatterplotUniforms: this.state.uniformStore.getManagedUniformBuffer(
this.context.device,
'scatterplot'
)
},
isInstanced: true
});
}
Expand Down