-
-
Notifications
You must be signed in to change notification settings - Fork 362
[WIP] Refactor Plot type & road to 1.0 #1085
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
Conversation
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.
Here's a preliminary review focusing just on your struct types. I am not sure I'll be able to provide much more useful feedback at this stage, but hopefully this helps at least somewhat.
Anything user-facing might need to stay generic, but if you're expecting a fair amount of transformation anyway (e.g., in terms of Float32 argtypes or something), then you might as well transform the container in addition to the eltype. For reference, here are the rules:
julia> isconcretetype(Vector{Float32})
true
julia> isconcretetype(AbstractVector{Float32})
false
julia> isconcretetype(Vector{Union{Nothing,Float32}})
true
julia> isconcretetype(Vector{Any}) # *elements* are not concretely-typed, but the *container* is
true
julia> isconcretetype(Vector{<:Real})
falseDeclaring something like
struct Scatter{N,A<:AbstractVector}
somefield::A
...is of course fine from the standpoint of inference of somefield (it will be concrete if the concrete Scatter is known), but problematic from the standpoint of more type-diversity among Scatter concrete types and thus longer latency.
There are cases where your AbstractVector field is the correct solution, but you should make sure you're very clear on why you're doing it that way. Basically that comes down to circumstances where you deliberately want to "hide the type" as a strategy to reduce latency. Under such circumstances you'd have carefully designed function-barriers that trigger runtime specialization on the specific AbstractVector. When deliberate (and in conjunction with some carefully-placed @nospecialize annotations), this can be an effective and powerful strategy, but it's not something you should do by accident.
| @@ -0,0 +1,43 @@ | |||
| @plottype mutable struct Lines{N} <: AbstractPlot{Any} | |||
| position::AbstractVector{Point{N,Float32}} | |||
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.
Does this have to be an AbstractVector? There are sometimes reason to choose that but I'm sure you know it will be non-inferrable. Since you're specifying the eltype so tightly, it seems likely this won't be user-facing anyway, and so can you just make this Vector?
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.
Yeah, that's up for debate... I would really like to keep it an AbstractVector, since there are quite a few use cases like efficiently mapping a view to a gpu buffer, or passing through GPUArrays and things like that...
But I guess we should benchmark the impact, and if it's huge, maybe try to stick to Vector as much as 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.
Would it help to make the type of positions field a type-parameter of Lines?
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.
You can keep it AbstractVector. The trick is either (1) to not pull it out of the Lines struct until the "last minute" and/or (2) annotate dispatches @nospecialize(position::AbstractVector{...}) until you deliberately want a function barrier (basically, for any method with a loop over the elements of position).
But the N type parameter will break @nospecialize, i.e., you can't expect foo(@nospecialize(position::AbstractVector{Point{N,Float32}})) where N to not fully specialize on position.
If your diversity of position types is not large, then this may not be a big problem. It sounds like you're going to become a @nospecialize expert soon 🙂 .
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.
Would it help to make the type of positions field a type-parameter of Lines?
It would improve inferrability at the cost of likely greater latency, because the compiler would have to specialize every call for every Scatter type you use.
| @@ -0,0 +1,36 @@ | |||
| const MarkerSizeTypes{N} = TorVector{Union{Float32, Vec{N, Float32}}} | |||
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.
It's remotely possible that it will be better to "flatten" the Union here, but let's wait and see how the inference works out (it's identical in consequence, so that's trivial to change later).
| @@ -0,0 +1,36 @@ | |||
| const MarkerSizeTypes{N} = TorVector{Union{Float32, Vec{N, Float32}}} | |||
| const MarkerTypes = TorVector{<:Union{Symbol,Char, Type{Circle}, Type{Rect2D}}} | |||
| const DistanceFieldType = Union{Nothing, AbstractMatrix{<: Union{Colorant, Number}}} | |||
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.
The more concrete you can make this, the better for inference & latency. But anything user-facing probably has to be general. You could always use
const DistanceFieldType = Union{Nothing, Matrix{<: Union{Colorant, Number}}}for struct fields and
const DistanceFieldTypeLike = Union{Nothing, <:AbstractMatrix{<: Union{Colorant, Number}}}for dispatch to indicate "something that could obviously be converted to a DistanceFieldType.
|
|
||
| const px = Pixel(1) | ||
|
|
||
| const Space = Type{<: Unit} |
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 will be rough for some of your previous field types...if it's possible to standardize internally...
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.
Yeah, I think this was just a stop gap solution, to not trigger a bigger refactor... I think this can just be an enum going forward..
| font::NativeFont = "Dejavue Sans" | ||
| strokecolor::ColorType = (:black, 0.0) | ||
| strokewidth::TorVector{Float32} = 0 | ||
| align::Vec2f0 = (:left, :bottom) |
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.
Hmm, how does this work? I can't imagine a method that doesn't involve type-piracy. Oh, unless the @plottype handles it.
| abstract type AbstractScreen <: AbstractDisplay end | ||
|
|
||
| const SceneLike = Union{AbstractScene, ScenePlot} | ||
| const TorVector{T} = Union{T, AbstractVector{T}} |
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.
Since this is used in struct fields, it sure would be nice to make this Vector{T} instead of AbstractVector 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.
Yaaah... 😢 I planned to Benchmark this when the PR is in a better state - but would be sad to give up on AbstractArray support, but maybe we can make it work one way or the other...
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.
You can of course have both TorAbstractVector and TorVector. If nothing else you might want to change the name to acknowledge the abstraction. (TorAVector would be pretty short if that's an issue.)
|
Will close this for now... Will bring back the important changes by cherry picking in much smaller PRs! |
This is a pretty big rewrite of the internal plot type.
The goal to not be breaking, though! This is a stretch goal, and quite a few things will likely be slightly breaking. One should think of this PR as Makie's 0.7 Julia version, on it's way to a breaking 1.0 release. This refactor will introduce warnings for features we want to deprecate, and will lay the groundwork to do the actual breaking changes.
The planned changes:
each atomic plot type now has a fixed set of fields with fixed types. This enables:
uninitilized_fields, and then get updated from the theme when actually drawnmake it possible to construct a plot type without a scene. This enables
revamp of observables inside plot types
setfield!operation will immediately converted the values to the strict field typesAttributestype, which was storing any plot attribute asObservable{Any}on_eventobservable, which one can use to register tosetfield!events, or other events like mouse/camera/axis-limit-changessetfield!convert_attributecalls in multiple parts of the pipelines. In the old infrastructure, everything got converted lazily, so there wasn't a clear point to convert.. Some some recipe did already apply conversions, and the backend then applied conversions againconversion pipeline in plot constructor
Scatter(1:4)to create the correct plot object, without a scene presentTimeline: