Open
Conversation
…, which can cause canvas is not properly cleared due to incorrect transform. It can be reproduced when both hover layered and zlevel is used. Close apache/echarts#18688 . (2) Clarity el.autoBatch and fix that batched rendering may not be executed in some edge cases.
… only style modifying, and fix the omission of __hoverStyle usage. Previously the hover layer effect is inconsistent and not preferable - when the original layer is required to change by the subsequent user interactions, the final composited effect will change unexpectedly due to the modification of el props. See test case in `echarts/test/hover-layer.html`.
…r for large data and progressive rendering - prevent unnecessary repeated hoverLayer rendering.
…er was likely to dirty and discard the rendered content, and restarted all drawing per frame, which probably caused the cumulative draw calls to significantly block the rendering in large data. This commit introduces more precise layer content reuse strategies: (1) Based on the recorded first element rather than the first displayList index of the last pass (which is likely to be changed by preceding irrelevant elements on underlying layers); (2) Support multiple runs of consecutive incremental elements to render into one singe layer without wrongly dirty each other - introduce data structure `LayerDrawCursor` per run of elements to render separately, and change `Displayable['incremental']` from boolean to number to designate different runs. The scenarios can be: echarts large bar and large candlestick series exist at the same time. (3) Previously hover layer was likely to be repeatedly rendered; fix that.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hover Layer
test case: echarts/test/hover-layer.html
Incremental / Progressive Rendering
In practice, previously incremental layer was likely to dirty and discard the rendered content, and restarted all drawing per frame, which probably caused the cumulative draw calls to significantly block the rendering in large data. This commit introduces more precise layer content reuse strategies:
LayerDrawCursorper run of elements to render separately, and changeDisplayable['incremental']from boolean to number to designate different runs. The scenarios can be: echarts large bar and large candlestick series exist at the same time.test case: echarts/test/stream-basic5.html echarts/test/stream-basic2.html echarts/test/stream-basic3.html echarts/test/stream-basic4.html echarts/test/stream-basic5.html
Current strategy:
Two use patterns are covered per incremental layer:
An single incremental element with a customized
buildPath, usingDisplayable['notClear']to retain the rendered content.
A run of consecutive incremental elements, progressively drawing per frame in
_paintList. Thisis not an optimal approach for rendering due to the increasing cost of updating and sorting
displayList. However, it support varying styles and can balance the cost between rendering andhit testing during hover (which may degrade with excessive points in single shape).
[LAYER_STACKING]
Layerinstance represents a physical layer, typically a HTML Canvas.zlevelwill be splitted to 2 or 3 physical layers if incremental elements occur,designated by
zlevel2. A full version can be like this:[[ layer_hover zlevel:100000 ]]
[[ layer_normal_above zlevel:0, zlevel2:2 (normal el after incremental el) ]]
[[ layer_incremental zlevel:0, zlevel2:1 (incremental el) ]]
[[ layer_normal_below zlevel:0, zlevel2:0 (normal el before incremental el) ]]
(But layer_normal_below may be omitted if not needed.)
zlevel, multiple runs of incremental elements share one physical layer (i.e.,zlevel2: 1).we do not support it.
To avoid excessive HTML Canvas creation, at most 3 layers can be created for a single
zlevel.If two runs of consecutive incremental elements are separated by some normal elements, those normal
elements are painted on
zlevel: 2, and all incremental elements are painted onzlevel: 1,regardless of
el.zandel.z2settings.Users can explicitly specify a higher
zlevelto allow more incremental layers to be created.determined by
add(el)order.[DISPLAY_LIST_SORTING_AND_LAYERING]
Currently there are 5 parameters to determine the layer and z-order for each element:
<zlevel, zlevel2, incremental(LayerDrawCursor), z, z2>
Only
zlevel,zandz2are user specified.The
displayListis sorted only byzlevel,zandz2.A <
zlevel,zlevel2> pair determines a layer.A
incremental(LayerDrawCursor)acts like a "soft layer", representing a run of consecutiveincremental elements. Multiple
LayerDrawCursors share one layer.Users must use different
el.incremental(a number) to distinguish different runs of consecutiveincremental elements. And each
el.incrementalhas its exclusiveLayerDrawCursor. Take echartsas an example: if there are multiple "series" requiring incremental, e.g., a bar series and a
candlestick series in a Cartesian, and their zlevel/z/z2 are typicall the same.
See LAYER_SAMPLE_CASE_3 for more details.
Consider sample cases below to check the implementation:
zlevel:5is explicitly specified by users.zlevel:0is the default.[[ layer_hover zlevel:100000 ]]
[[ layer_normal_above_2 zlevel:5, zlevel2:2 ]]
[[ layer_incremental_2 zlevel:5, zlevel2:1 ]]
[[ layer_normal_below_2 zlevel:5, zlevel2:0 ]]
[[ layer_normal_above_1 zlevel:0, zlevel2:2 ]]
[[ layer_incremental_1 zlevel:0, zlevel2:1 ]]
[[ layer_normal_below_1 zlevel:0, zlevel2:0 ]]
No elements are before incremental elements.
[[ layer_hover zlevel: 100000 ]]
[[ layer_normal_above zlevel:0, zlevel2:2 ]]
[[ layer_incremental zlevel:0, zlevel2:1 ]]
Multiple runs of consecutive incremental elements, may (or not) be separated by some normal elements.
Suppose a sorted
displayListis:[{a_nor}, {b_inc:7}, {c_inc:7}, {d_nor}, {e_inc:9}, {f_inc:9}, {g_nor}].Then both incremental:7 and incremental:9 have new elements added.
The sorted
displayListbecome:[{a_nor}, {b_inc:7}, {c_inc:7}, {m_inc:7}, {d_nor}, {e_inc:9}, {f_inc:9}, {n_inc:9}, {g_nor}].The order can not match the original
displayList- new elements are inserted in the middle ratherthan at the end. Therefore, multiple
layerDrawCursors are introduced to manage the pointers separately,enabling them to share one physical layer.
They are arranged into layers like this:
[[ layer_hover zlevel:100000 ]]
[[ layer_normal_above zlevel:0, zlevel2:2 layerDrawCursor:0 {d_nor}, {g_nor} ]]
[[ layer_incremental zlevel:0, zlevel2:1 layerDrawCursor:9 {e_inc:9}, {f_inc:9} {n_inc:9} ]]
[[ layerDrawCursor:7 {b_inc:7}, {c_inc:7} {m_inc:7} ]]
[[ layer_normal_below zlevel:0, zlevel2:0 layerDrawCursor:0 {a_nor} ]]
[LAYER_DIRTY_RULES]:
Only dirty layer will be cleared and repaint later.
layer.__dirtyis set by:For normal layers, currently REDRAW_BIT is the only reliable way to make sure repainting, since
reorder is not checked. So we conservatively always dirty the layers if any REDRAW_BIT occur.
For incremental layers, we aggressively dirty the layer only if drawn elements have REDRAW_BIT,
since redorder of incremental elements hardly occurs.
layerDrawCursor.firstandlayerDrawCursor.endIdx.[LAYER_CONTENT_RETAINED]:
This strategy is mainly required by progressive rendering, where typicall new elements are
appended, and repaint from the start per frame should be prevented. Otherwise, increasing
draw calls can significantly block rendering. Additionally, If displayList indices of incremental
elements are changed due to preceding elements of other layers, the drawing should not be restarted.
Therefore, we record the first element to shift indices for this case.
[LAYER_FAIL_TO_DIRTY_IF_ONLY_REORDER]:
This strategy has also been applied to normal layers to prevent them from repainting in progressive
frames. Upstream applications should remain the order of elements unchanged if no REDRAW_BIT is
set - no checking for this currently. Otherwise, layers fail to dirty unexpectedly.
Take echarts as an example, consider common patterns: "remove some elements", "modify z/z2 typically
via useState", "clear and recreate all elements per user interaction", "reuse elements if possible
but update attributes and styles per user interaction". Layer dirty can be triggered. If bad cases
occur, more mechanism can be introduced (e.g., record el ids in layerDrawCursor for checking).
PENDING:
In INCREMENTAL_CASE_MULTIPLE_ELEMENTS, displayList sorting and
_updateAndAddDisplayablewill beexecuted per frame and significantly consume time in high element counts (indicatively, 1e6 in
certain environments). Perhaps displayList can be separated by Layer or by LayerDrawCursor,
and perform targeted optimization - omitting unnecessary sorting and
update().el.incrementalto automatically ensure consecutive?Currently, the contiguity can only be ensured by the order of
add()call.