-
-
Notifications
You must be signed in to change notification settings - Fork 313
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
[WIP] Refactor Plot type & road to 1.0 #1085
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})
false
Declaring 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
.
@@ -68,6 +64,12 @@ end | |||
|
|||
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 typesAttributes
type, which was storing any plot attribute asObservable{Any}
on_event
observable, which one can use to register tosetfield!
events, or other events like mouse/camera/axis-limit-changessetfield!
convert_attribute
calls 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: