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

symbol-clip: Render tests, debug page and distance matrix rework #11065

Merged
merged 67 commits into from
Oct 1, 2021

Conversation

arindam1993
Copy link
Contributor

@arindam1993 arindam1993 commented Sep 29, 2021

This PR adds a bunch of render tests testing out various different code paths, I've grouped them at the first level by the type of symbol-placement used, so there are two folders point and line.
The tests in those folders use the same filter expression, but with different data Point features for the point placement and LineString features for line placement.

I also implemented a new set of centerDistanceMatrices in Transform to account for the small differences we need between the fog and symbol distance matrices. These differences are

  • Its centered at z=0 and is used to calculate 2D distances along the sea-level plane.
  • Its uses a simpler reference frame, the distance d calculation for any anchor point p ends up being
v = p * distance_matrix 
d = sign(v.y) * length(v)

This is because the matrix defines a co-ordinate system centered at the map center with +y pointing away and +x pointing to the right.

I also added a debug page that uses this matrix + turf to generate some geojson that draws out the distance scale as an overlay.

Arindam Bose added 7 commits September 29, 2021 14:57
…amic-filter/render-tests

# Conflicts:
#	src/style-spec/expression/index.js
#	src/style-spec/feature_filter/index.js
#	src/style-spec/reference/v8.json
#	src/style/evaluation_parameters.js
#	src/symbol/placement.js
#	src/ui/map.js
}
}
return ['main'];
}))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

threw in this fix for the microbenchmarks as well.

src/geo/transform.js Outdated Show resolved Hide resolved
@SnailBones
Copy link
Contributor

Good work Arindam, I like the thorough tests!

One nitpicky question: can you explain the decision to put the new tests in such deeply nested folders? Do you expect us to add inside of dynamic-filter in a sibling directory to symbols?

I like the increased organization, but it's also bugging me a bit that it's inconsistent with the other tests (Others seem to be nested two directories deep, these new ones are four or five).

Co-authored-by: Aidan H <aidan.hendrickson@mapbox.com>
@arindam1993
Copy link
Contributor Author

arindam1993 commented Sep 30, 2021

Good work Arindam, I like the thorough tests!

One nitpicky question: can you explain the decision to put the new tests in such deeply nested folders? Do you expect us to add inside of dynamic-filter in a sibling directory to symbols?

I like the increased organization, but it's also bugging me a bit that it's inconsistent with the other tests (Others seem to be nested two directories deep, these new ones are four or five).

mostly to avoid extra long names like this 😅
Screen Shot 2021-09-30 at 1 00 31 PM

In the future if we add a feature to have a foreground clipping thread for say, fill-extrusions then I'd add a fill-extrusion sibling directory and expand out all the combinatorial possibilities from there.
Here the combinations expand out this way,
layer-type -> symbol-placement-type (point/line) -> expression-type -> (optional) multiple global states of same expression (varying pitch)

Copy link
Contributor

@SnailBones SnailBones left a comment

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

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

Things look good on a detailed level, but I did want to clarify my understanding of the precise definition(s) of distance with respect to clipping before giving it a final 👍

@@ -52,10 +54,11 @@ class EvaluationContext {
}

distanceFromCamera() {
Copy link
Contributor

@rreusser rreusser Sep 30, 2021

Choose a reason for hiding this comment

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

Is distanceFromCamera a descriptive name? It seems like this is used to compute the distance from the view center/look at rather than the distance from the camera.

What catches my attention is that the difference seems to add a strong radial dependence to the cutoff. I don't immediately see the pros/cons of the strong radial dependence, as illustrated in the highlighted region below. Is this the main criteria people would use to clip based on distance? I'm not opposed to it and can imagine that some of the factors which made fog challenging may factor into the decision, but I'm at least weighing the mental overhead of another similar but slightly differently defined distance field. Are there reasons not to use a distance metric similar in definition to fog?

distance-to-center < -0.5:

less-than-0 5

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because this is how the fog distance field shifts around with pitch (this isnt a problem for fog because we only use it at high pitch where it doesnt shift) The shift is because of the translation the camera when pitching.
At pitch 0:
Screen Shot 2021-09-30 at 3 48 31 PM
At pitch 20:
Screen Shot 2021-09-30 at 3 48 48 PM
At pitch 60:
Screen Shot 2021-09-30 at 3 49 10 PM
At pitch 80:
Screen Shot 2021-09-30 at 3 49 24 PM

Whereas this stays locked at the center.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is what I used to generate the geojson for the above viz

function generateDistanceScales() {
    const center = map.getCenter();
    const bearing = map.getBearing();
    const rings = 20;
    const step = 0.1;

    const matrix = glMatrix.mat4.invert([], map.transform.mercatorFogMatrix);
    const circles = [];
    for (let i = 1; i <= rings; i++) {
        const radius = step * i;

        for(let th = 0; th < 360; th+= 20) {
            const p = [radius * Math.sin(th * Math.PI/180), radius * Math.cos(th * Math.PI/180), 0];
            const coord = new mapboxgl.MercatorCoordinate(...glMatrix.vec3.transformMat4([], p, matrix)).toLngLat();
            const f = turf.point([coord.lng, coord.lat]);
            f.properties['distance'] =  `${(radius - 0.5 / Math.tan(map.transform.fov * 0.5)).toFixed(2)}`;
            circles.push(f);
        }
    }

    return turf.featureCollection(circles);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

@arindam1993 What's the advantage of this staying locked at the center? The fog behavior seems closer to a distance from camera operation. Wouldn't that be more intuitive for users than the radial behavior from map center, particularly if it could be consistent with fog?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because unlike fog this isn't a high-pitch only use-case, users can do whatever they want with the pitch and set much lower thresholds too if they wish. So imo having a nice and fixed reference frame is better so that the clipping boundaries arent moving around as much when pitching in the range of (0-40) deg.

Copy link
Contributor

@rreusser rreusser Oct 1, 2021

Choose a reason for hiding this comment

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

I've created a notebook here which illustrates fog units (which would be my vote to reuse as symbol-clip units as well): https://observablehq.com/d/50ec70cf44480b1d

The behavior at medium and high pitch seems preferable to me. The units make a little less sense at very low pitch, though the symbol-clip feature also makes a lot less sense to me at zero pitch as well. It would be straightforward to transition to viewport-fixed units (viewport size = 1.0) at zero pitch so that they would always make sense, but again, I'd want to be certain of the use case of symbol-clip at zero pitch before strongly suggesting that.

Also, regarding the fog elevation estimation, I'd imagine we could neglect elevation sampling for this distance metric when terrain is enabled, and similar issues like the units falling away to the horizon at high pitch in high-elevation terrain would be much, much, much less apparent than fog.

fog-pitch-units

Copy link
Contributor

@rreusser rreusser Oct 1, 2021

Choose a reason for hiding this comment

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

Talked to Arindam. One downside of the above is that it adds additional dependence on pitch, which would be nice to avoid since it'll make symbols hide/show more often when pitching. We're currently trying out ground-fixed planar lines (no pitch-dependence).

Copy link
Contributor

@SnailBones SnailBones Oct 1, 2021

Choose a reason for hiding this comment

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

I'm still struggling to understand the difference between how these approaches will effect symbol filtering. Do we have an example that more closely reflects real-world use case of this feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got the planar lines implemented and tests updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@SnailBones the problem is the foreshortening of the far distances.
Sample case:
filter: [ '<', ['distance-from-center'], 0.5]
Here the value of 0.5 means that: Clip this symbol in the region beyond what would be the top of the screen at 0 pitch

But since the fog distance measures a 3d distance from the camera to the plane, the value of the distance at the same geographic location increases as pitch increases, which means that what wasn't being clipped at 0 pitch gets clipped at a higher pitch. You can see this in the GIF @rreusser posted, the first line corresponds to fog_distance=0.5, it starts of at the water at 0 pitch and then gets squished inwards.

Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

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

Looks good! Nice work. 👏 Presumably this will suffer from some of the same challenges as fog when terrain is active, but I assume it simply won't matter since it'll be so much harder to notice.

const posMatrix = this.calculatePosMatrix(unwrappedTileID, this.cameraWorldSize);
mat4.multiply(posMatrix, this.centerDistanceMatrix, posMatrix);

cache[fogTileMatrixKey] = new Float32Array(posMatrix);
Copy link
Contributor

Choose a reason for hiding this comment

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

It's been on my todo list for a while to switch these all to plain Arrays so that they'd be double precision with GL also still accepting them.

@arindam1993
Copy link
Contributor Author

Thanks for all the feedback 🙇 , the switch the planar lines was a really good move!

@arindam1993 arindam1993 merged commit 7fe14b8 into symbol-clip Oct 1, 2021
@arindam1993 arindam1993 deleted the dynamic-filter/render-tests branch October 1, 2021 22:30
arindam1993 pushed a commit that referenced this pull request Oct 7, 2021
…rom-camera` expressions (#10795)

* Setup plumbing for pitch and distance-from-camera expressions

* Add symbol-clip with support for pitch and distance-from-camera expressions

* hackily working pitch expression

* distance clipping

* Track clipped state in JointPlacement and JointOpacity, reflect clipping using notUsed state in debug buffers

* Rename distance-from-camera to distance-from-center

* symbol-clip: splitting filter expressions into dynamic and static parts (#10923)

* symbol-clip: dynamic filter style spec changes (#10977)

* symbol-clip: Render tests, debug page and distance matrix rework (#11065)

* WIP dynamic filter splitting stage 1

* Add tests for isDynamicFilter

* More test cases for isDynamicFilter

* Existing static filters get passed through unmodified.

* WIP extracting static filters

* Working case -> any translation

* More tests for case -> any conversion

* Add support for match branches

* WIP: V0 of adding collapsing of dynamic expressions to true

* more test cases

* tests and some more refactoring

* remove temory inspection code from unit tests

* Fix dynamic filter detection

* Fix failing spec test

* Units tests hopefully :green: now

* Add test to cover for `null` in expressions

* Address CR comments and reduce number of temporary allocations

* remove gl-matrix as dependency of style-spec and inline a matrix multiplication function

* Fix lint issues

* Add better error messages for expression compilation failures

* Ensure location parameter is passed down

* Remove symbol-clip from the style-spec

* Remove unused property-expression type

* Move filterSpec to v8.json and replace symbol-clip with dynamic filter

* Fix unit tests

* Fix most flow errors

* finally flow is happy

* Add expression validation code

* Add api-supported tests for validation

* Fix some failing unit tests

* Ensure layer._featureFIlter is updated on main thread as well

* Ensure layerType is passed down to validation

* Fix flow and linting

* Fix unit tests

* Pass 1 of addressing CR comments

* Add basic placementTime metric

* Move to using total placement time

* Fix lint errors

* simplify placement time tracking

* Fix silent conflict in with SymbolInstanceStruct changes

* try benchmarking with filter splitting algorithm removed

* Fix versions microbenchmark page

* Revert "try benchmarking with filter splitting algorithm removed"

This reverts commit 865f354.

* Simplify by calculating distance using tile coordinates of the symbol directly

* Use new specific centerDistanceMatrix

* Sign flipping for consistency

* add new debug page with distance visualizer

* First set of pure distance based render tests

* Pitch thresholding tests

* Fix lint errors

* More tests

* Add first batch of point symbol render tests

* Increase threshold

* Increase allowed threshold for the correct tests 🤦

* remove flaky collision debug boxes instead

* Move geojson test data to be in separate files instead of inlined into styles

* Update flaky render test expectation

* Fix distanceMatrix comment

Co-authored-by: Aidan H <aidan.hendrickson@mapbox.com>

* Switch to linear scale and update line placement tests

* Update point placement tests

* Update test expectations

* Fix lint error

* Remove flaky collision boxes

Co-authored-by: Aidan H <aidan.hendrickson@mapbox.com>

* Remove frametime logging change

* Ensure that feature deserialization happens only when needed

* Fix matrixKey naming leftover from copying fogMatrix code

Co-authored-by: Karim Naaji <karim.naaji@gmail.com>

* Rename matrices as per CR comments

* Default layerType inside filter validation function.

* add `VectorTileFeature` deserialization cache

* Switch to matrix-free distance calculation

* Add cache in Transform

* Precompute bearing vector

* prescale by windowScaleFactor

* Inline `getSymbolFeature`

* Lazy filter compilation

* Move distance matrix calculation out to the debug page

Co-authored-by: Aidan H <aidan.hendrickson@mapbox.com>
Co-authored-by: Karim Naaji <karim.naaji@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants