Bug 146475 - Web Inspector: Rendering Frame bars appear misaligned and contain gaps when displaying small task segments
Summary: Web Inspector: Rendering Frame bars appear misaligned and contain gaps when d...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Inspector (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Matt Baker
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2015-06-30 14:58 PDT by Matt Baker
Modified: 2015-07-06 17:24 PDT (History)
9 users (show)

See Also:


Attachments
[Image] Subpixel problems (65.69 KB, image/png)
2015-06-30 14:58 PDT, Matt Baker
no flags Details
[Image] Results after applying new algorithm (38.62 KB, image/png)
2015-07-01 18:29 PDT, Matt Baker
no flags Details
[Patch] Proposed Fix (11.89 KB, patch)
2015-07-06 09:26 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (12.65 KB, patch)
2015-07-06 13:28 PDT, Matt Baker
no flags Details | Formatted Diff | Diff
[Patch] Proposed Fix (12.68 KB, patch)
2015-07-06 16:23 PDT, Matt Baker
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Baker 2015-06-30 14:58:20 PDT
Created attachment 255859 [details]
[Image] Subpixel problems

* SUMMARY
Rendering Frame bars appear misaligned and contain gaps when displaying small task segments.

The Rendering Frames overview graph has a fixed height of 108px. Frame time is shown on the vertical axis with a minimum/maximum height of 0.0185 s and 0.037 s. Thus each pixel on the y-axis has a value between 0.171 ms - 0.342 ms.

The duration of a single task can frequently fall below this resolution, resulting in subpixel segment heights. After rasterization and applying CSS styles (our 1px segment separator), these small tasks are invisible, appearing as gaps in the frame. It is especially noticeable when the gap occurs at the bottom of the frame, making it appear to float above the horizontal axis (see screenshot).

* NOTE
Some tasks will inevitably be too small to display. The ideal algorithm would:

1. Throw out tasks that are too small.
2. Pad tasks that are very close to the minimum resolution in order to display them (borrowing "rounded" time from larger tasks).
3. Show all frames, regardless of how small. A brief frame that would be less than a pixel high should be padded to a minimum frame height.
4. Keep the overall frame height and ratio of displayed tasks as accurate as possible.
Comment 1 Radar WebKit Bug Importer 2015-06-30 14:59:06 PDT
<rdar://problem/21620063>
Comment 2 Matt Baker 2015-07-01 18:29:08 PDT
Created attachment 255980 [details]
[Image] Results after applying new algorithm

Three timeline recordings rendered with the new frame-segment algorithm. Shown alongside the same data rendered with the current algorithm for comparison.
Comment 3 Matt Baker 2015-07-06 09:26:36 PDT
Created attachment 256217 [details]
[Patch] Proposed Fix
Comment 4 Joseph Pecoraro 2015-07-06 12:19:49 PDT
Comment on attachment 256217 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256217&action=review

Sounds good! r=me

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:99
> +        var currentSegment;

Style: lets do an = null.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:116
> +            if (currentSegment.duration < secondsPerBlock)
> +                invisibleSegments.push({segment: segments.lastValue, index: segments.length - 1});

Nit: segments.lastValue at this point could just be "currentSegment".

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:156
> +        // After grouping sub-block tasks, a second is needed to handle those groups that are still beneath the minimum

Grammaro: "a second is needed" => "a second pass is needed"

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:186
> +            }, this);

Unnecessary "this".

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:197
> +        }, this);

Unnecessary "this".

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:227
> +        this._record.__displayData.segments.forEach(function(segment) {
> +            var element = document.createElement("div");
> +            this._updateElementPosition(element, segment.duration / this._record.__displayData.frameDuration, "height");
> +            element.classList.add("duration", segment.taskType);
> +            frameElement.insertBefore(element, frameElement.firstChild);
>          }, this);

I wonder if it would be better to append in reverse order to avoid a whole bunch of insertBefores? I'm not sure if there is a possible performance cost, especially for very small lists (1-3).
Comment 5 Brian Burg 2015-07-06 12:30:54 PDT
Comment on attachment 256217 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256217&action=review

> Source/WebInspectorUI/UserInterface/Base/Utilities.js:804
> +        return Math.round(num  / step) * step;

weird spacing.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:170
> +            var available = previous && next ? previous.remainder + next.remainder : (previous ? previous.remainder : next.remainder);

Ow, my brain. Try to be consistent about naming (i.e., availableDuration, previousSegment, nextSegment)

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:173
> +                var targetSegment = previous && next ? (previous.duration > next.duration ? previous : next) : previous || next;

Ow, my brain.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:179
> +            var neighbors = previous && next ? (previous.remainder > next.remainder ? [previous, next] : [next, previous]) : [previous || next]

I've written this type of line before. You will regret it next year.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:199
> +        return {frameDuration, segments};

Cool!
Comment 6 Matt Baker 2015-07-06 13:02:09 PDT
Comment on attachment 256217 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256217&action=review

>> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:179
>> +            var neighbors = previous && next ? (previous.remainder > next.remainder ? [previous, next] : [next, previous]) : [previous || next]
> 
> I've written this type of line before. You will regret it next year.

I agree, nested ternaries are pretty nasty. I'll rewrite them as if-else for clarity.
Comment 7 Matt Baker 2015-07-06 13:28:10 PDT
Created attachment 256235 [details]
[Patch] Proposed Fix
Comment 8 Timothy Hatcher 2015-07-06 14:50:38 PDT
Comment on attachment 256235 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256235&action=review

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:104
> +            }
> +            var roundedDuration = Math.roundTo(segment.duration, secondsPerBlock);

Nit: Newline between these lines.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:123
> +        Object.keys(WebInspector.RenderingFrameTimelineRecord.TaskType).forEach(function(key) {

for (var key in WebInspector.RenderingFrameTimelineRecord.TaskType) {

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:140
> +            }
> +            if (currentSegment.duration >= secondsPerBlock)

Nit: Newline between these lines.

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:159
> +        invisibleSegments.forEach(function(item) {

for (var item of invisibleSegments) {

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:238
> +        this._record.__displayData.segments.forEach(function(segment) {

for (var segment of this._record.__displayData.segments)
Comment 9 Matt Baker 2015-07-06 16:23:54 PDT
Created attachment 256253 [details]
[Patch] Proposed Fix
Comment 10 Timothy Hatcher 2015-07-06 16:32:32 PDT
Comment on attachment 256253 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256253&action=review

> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:192
> +            adjacentSegments.forEach(function(adjacentSegment) {

Not: Could have also been converted to for (..of..).
Comment 11 Matt Baker 2015-07-06 16:54:32 PDT
Comment on attachment 256253 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256253&action=review

>> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:192
>> +            adjacentSegments.forEach(function(adjacentSegment) {
> 
> Not: Could have also been converted to for (..of..).

What are our style guidelines with regard to Array.prototype.forEach? I see it being used with and without the optional thisArg.
Comment 12 Timothy Hatcher 2015-07-06 17:04:09 PDT
Comment on attachment 256253 [details]
[Patch] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=256253&action=review

>>> Source/WebInspectorUI/UserInterface/Views/TimelineRecordFrame.js:192
>>> +            adjacentSegments.forEach(function(adjacentSegment) {
>> 
>> Not: Could have also been converted to for (..of..).
> 
> What are our style guidelines with regard to Array.prototype.forEach? I see it being used with and without the optional thisArg.

We prefer not to use it now that we have for (..of..). That avoided a lot of function calls and closures.
Comment 13 WebKit Commit Bot 2015-07-06 17:24:07 PDT
Comment on attachment 256253 [details]
[Patch] Proposed Fix

Clearing flags on attachment: 256253

Committed r186387: <http://trac.webkit.org/changeset/186387>
Comment 14 WebKit Commit Bot 2015-07-06 17:24:13 PDT
All reviewed patches have been landed.  Closing bug.