-
Notifications
You must be signed in to change notification settings - Fork 6
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
Implemented ScatterplotLayer #124
Conversation
ef25af7
to
6ea58da
Compare
6ea58da
to
cbe4516
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice, this is progressing really fast now!
cpp/examples/deck.gl/flight-paths.cc
Outdated
auto createScatterplotLayer(const std::string &dataPath) -> std::shared_ptr<ScatterplotLayer::Props> { | ||
auto scatterplotLayerProps = std::make_shared<ScatterplotLayer::Props>(); | ||
scatterplotLayerProps->id = "airports"; | ||
scatterplotLayerProps->getPosition = [](const Row &row) -> mathgl::Vector3<float> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use return type deduction to minimize the amount of boilerplate
auto [](const Row &row) { return row.getVector3<float>("coordinates"); };
The method is already calling out the return type (which is also why I was softly lobbying for templates, responding to question in another PR).
If we just call the helper variable props
we may be able to fit it into one line, which would increase readability (at least according to my personal aestethics).
@@ -67,10 +67,10 @@ class LineLayer::Props : public Layer::Props { | |||
return new LineLayer{std::dynamic_pointer_cast<LineLayer::Props>(props)}; | |||
} | |||
|
|||
std::string widthUnits{"pixels"}; // 'pixels', | |||
float widthScale{1}; // {type: 'number', value: 1, min: 0}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We haven't captured the min values in the prop types, so we can't check those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How should we deal with these kind of ranges in general? Is there a way to get these clamped implicitly without relying on a backing type?
@@ -140,6 +114,9 @@ void ScatterplotLayer::initializeState() { | |||
auto lineWidth = std::make_shared<arrow::Field>("instanceLineWidths", arrow::float32()); | |||
auto getLineWidth = std::bind(&ScatterplotLayer::getLineWidthData, this, std::placeholders::_1); | |||
this->attributeManager->add(garrow::ColumnBuilder{lineWidth, getLineWidth}); | |||
|
|||
// TODO(ilija@unfolded.ai): Where should we initialize models? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, creating Models in initializeState is what we do. Because at this time, the layerContext has been assigned so WGPU device etc is available at this time.
auto lineWidthScale = props->lineWidthScale * widthMultiplier; | ||
|
||
ScatterplotLayerUniforms layerUniforms{ | ||
props->opacity, props->radiusScale, props->radiusMinPixels, props->radiusMaxPixels, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Long lists of positional parameters of same type (float etc) looks very error prone as the compiler can only do limited checking.
Either use the C++20 designated initializers or assign one by one?
for (auto const& model : this->getModels()) { | ||
// Layer uniforms are currently bound to index 1 | ||
model->setUniforms( | ||
std::make_shared<garrow::Array>(this->context->device, &layerUniforms, 1, wgpu::BufferUsage::Uniform), 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like we are creating and destroying a lot of GPU buffers.
Fine for now just to get rendering working, but longer term, GPUBuffers should be populated in updateState if possible so that they are ready to just set here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved buffer creation, data setting and binding across initialize
/update
/draw
state methods.
We now create a single buffer that gets filled with data, which works well except that updateState
isn't called when viewport updates (is that supposed to be the case?), so widthMultiplier
isn't correct.
Temporarily made it so that updateState
is called from drawState
, which can easily be removed once diffing/dirty flags work correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now create a single buffer that gets filled with data, which works well except that updateState isn't called when viewport updates (is that supposed to be the case?), so widthMultiplier isn't correct.
I suggest we design this somewhat independently from JS. The JS API didn't moved all uniform updating into updateState. It also implements the "uniform animation" feature in a JavaScript dependent way.
Updating when viewport changes can trigger a lot of updates, e.g. when using multiple views we get multiple updates every frame. So we want to minimize calls if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I fully understand where we should be doing the updates then, let's sync on this one
@@ -152,7 +152,7 @@ auto Model::_createBindGroupLayout(wgpu::Device device, const std::vector<Unifor | |||
-> wgpu::BindGroupLayout { | |||
std::vector<wgpu::BindGroupLayoutBinding> bindings; | |||
for (uint32_t i = 0; i < uniforms.size(); i++) { | |||
auto binding = wgpu::BindGroupLayoutBinding{i, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer, | |||
auto binding = wgpu::BindGroupLayoutBinding{i, uniforms[i].shaderStage, wgpu::BindingType::UniformBuffer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could just default this to always assign to both, so apps don't need to track this? Or do we get warnings and inefficiencies if we assign it but then it is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not what the implications of doing that would be, if there's an additional overhead (I assume there is, else it would be done by default?) I'd prefer we declare it explicitly. If not we can of course get rid of it
An initial key observation is that the "circles" in the scatterplot layer are created in the fragment shader, not the vertex shader. I.e. we do not have a geometry that approximates a circle. We have a square geometry, and we discard any pixels that are at It is then worth verifying that this calculation/approach is still working here - the code looks OK from a quick scan, but worth poking around commenting things out etc and make sure it has the intended effect. Also, a trivial mistake could have been made when converting the geometry. It should be a "unit" square.
Assuming you are talking about the outline around the circles, that is a fairly recent addition to the scatterplotlayer and I don't know if any examples that use it (@Pessimistress may know more) |
6e8cfe6
to
35be8bc
Compare
Addressed pending comments. Spent some time debugging potential issues with circle rounding, it seems to be a precision problem, where we'd end up with a larger radius than we're supposed to, leaving us with a circle that isn't rounded perfectly. Changing I am not sure what other implications updating that value might have, so I left it as-is, but that's the only thing I found that looked suspicious. Will revisit based on feedback there, and merge this to avoid code pilling up |
Two things that I had to cover that are specific to this layer:
That being said I don't think these look the way they're supposed to look, they seem to be squares instead of circles that I'm seeing on the web. Also, when zooming in very closely, they seem to stop scaling at some point. I'm seeing weird geometry in the geometry inspector (could just be a bug in the tool, it's pretty flimsy sometimes) although all of the values seem valid, no NaNs or anything.
I also did not test the line component, are there any examples that make use of it?