diff options
author | Lalit Maganti <lalitm@google.com> | 2024-05-07 12:53:20 +0100 |
---|---|---|
committer | Lalit Maganti <lalitm@google.com> | 2024-05-07 12:53:20 +0100 |
commit | 7e9067b9f61ffa461f464d9ca22219bff3b8826b (patch) | |
tree | 30fa268a55280abaaf404d7cf99038017d98ec25 | |
parent | 16f039d5ac2c7a6aa93c1dfc65af3fd6ece7e571 (diff) | |
download | perfetto-7e9067b9f61ffa461f464d9ca22219bff3b8826b.tar.gz |
ui: make sure data fetch bounds are aligned to resolution
This CL changes the TimelineFetcher to ensure that the bounds it requests
is a multiple of the resolution. If this is not the case, it can led to
very subtle rendering artifacts: this is especially bad if the no row is
shown when it should be.
Change-Id: I01e088d6cd8d886d6b0b8e8a6285d4e0ff53f67f
Bug: 338126907
-rw-r--r-- | test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 | 2 | ||||
-rw-r--r-- | ui/src/common/track_helper.ts | 12 | ||||
-rw-r--r-- | ui/src/frontend/base_slice_track.ts | 5 | ||||
-rw-r--r-- | ui/src/tracks/cpu_slices/index.ts | 49 |
4 files changed, 36 insertions, 32 deletions
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 index aa136cdce..1375a4a5c 100644 --- a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 +++ b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 @@ -1 +1 @@ -b2c625bbdeb611de9a4bfbc3497353ee91322410bb867eee603bf2635353a21f
\ No newline at end of file +3194c35d4ec8eaf318fa2edcbf8ba04a9d29b0093fd60248ee0c73e982a1766e
\ No newline at end of file diff --git a/ui/src/common/track_helper.ts b/ui/src/common/track_helper.ts index 4adeee62e..0b58e01f1 100644 --- a/ui/src/common/track_helper.ts +++ b/ui/src/common/track_helper.ts @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {BigintMath} from '../base/bigint_math'; import {Disposable} from '../base/disposable'; import {duration, Time, time, TimeSpan} from '../base/time'; export {Store} from '../base/store'; @@ -62,9 +63,14 @@ export class TimelineFetcher<Data> implements Disposable { async requestData(timespan: TimeSpan, resolution: duration): Promise<void> { if (this.shouldLoadNewData(timespan, resolution)) { // Over request data, one page worth to the left and right. - const start = Time.sub(timespan.start, timespan.duration); - const end = Time.add(timespan.end, timespan.duration); - this.latestTimespan = new TimeSpan(start, end); + const start = timespan.start - timespan.duration; + const end = timespan.end + timespan.duration; + + // Quantize up and down to the bounds of |resolution|. + const startQ = Time.fromRaw(BigintMath.quantFloor(start, resolution)); + const endQ = Time.fromRaw(BigintMath.quantCeil(end, resolution)); + + this.latestTimespan = new TimeSpan(startQ, endQ); this.latestResolution = resolution; await this.loadData(); } diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts index 39c38e598..7e013ccc1 100644 --- a/ui/src/frontend/base_slice_track.ts +++ b/ui/src/frontend/base_slice_track.ts @@ -687,11 +687,12 @@ export abstract class BaseSliceTrack< ); } + const resolution = rawSlicesKey.bucketSize; const extraCols = this.extraSqlColumns.join(','); const queryRes = await this.engine.query(` SELECT - (z.ts / ${rawSlicesKey.bucketSize}) * ${rawSlicesKey.bucketSize} as tsQ, - max(z.dur, ${rawSlicesKey.bucketSize}) as durQ, + (z.ts / ${resolution}) * ${resolution} as tsQ, + ((z.dur / ${resolution}) + 1) * ${resolution} as durQ, s.ts as ts, s.dur as dur, s.id, diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts index 5c042dc9d..139bac5c3 100644 --- a/ui/src/tracks/cpu_slices/index.ts +++ b/ui/src/tracks/cpu_slices/index.ts @@ -47,8 +47,8 @@ export const CPU_SLICE_TRACK_KIND = 'CpuSliceTrack'; export interface Data extends TrackData { // Slices are stored in a columnar fashion. All fields have the same length. ids: Float64Array; - starts: BigInt64Array; - ends: BigInt64Array; + startQs: BigInt64Array; + endQs: BigInt64Array; utids: Uint32Array; flags: Uint8Array; lastRowId: number; @@ -112,8 +112,8 @@ class CpuSliceTrack implements Track { const queryRes = await this.engine.query(` select - (z.ts / ${resolution}) * ${resolution} as ts, - max(z.dur, ${resolution}) as dur, + (z.ts / ${resolution}) * ${resolution} as tsQ, + (((z.ts + z.dur) / ${resolution}) + 1) * ${resolution} as tsEndQ, s.utid, s.id, s.dur = -1 as isIncomplete, @@ -130,26 +130,23 @@ class CpuSliceTrack implements Track { length: numRows, lastRowId: this.lastRowId, ids: new Float64Array(numRows), - starts: new BigInt64Array(numRows), - ends: new BigInt64Array(numRows), + startQs: new BigInt64Array(numRows), + endQs: new BigInt64Array(numRows), utids: new Uint32Array(numRows), flags: new Uint8Array(numRows), }; const it = queryRes.iter({ - ts: LONG, - dur: LONG, + tsQ: LONG, + tsEndQ: LONG, utid: NUM, id: NUM, isIncomplete: NUM, isRealtime: NUM, }); for (let row = 0; it.valid(); it.next(), row++) { - const start = it.ts; - const dur = it.dur; - - slices.starts[row] = start; - slices.ends[row] = start + dur; + slices.startQs[row] = it.tsQ; + slices.endQs[row] = it.tsEndQ; slices.utids[row] = it.utid; slices.ids[row] = it.id; @@ -201,8 +198,8 @@ class CpuSliceTrack implements Track { renderSlices(ctx: CanvasRenderingContext2D, data: Data): void { const {visibleTimeScale, visibleTimeSpan, visibleWindowTime} = globals.timeline; - assertTrue(data.starts.length === data.ends.length); - assertTrue(data.starts.length === data.utids.length); + assertTrue(data.startQs.length === data.endQs.length); + assertTrue(data.startQs.length === data.utids.length); const visWindowEndPx = visibleTimeScale.hpTimeToPx(visibleWindowTime.end); @@ -213,15 +210,15 @@ class CpuSliceTrack implements Track { const startTime = visibleTimeSpan.start; const endTime = visibleTimeSpan.end; - const rawStartIdx = data.ends.findIndex((end) => end >= startTime); + const rawStartIdx = data.endQs.findIndex((end) => end >= startTime); const startIdx = rawStartIdx === -1 ? 0 : rawStartIdx; - const [, rawEndIdx] = searchSegment(data.starts, endTime); - const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx; + const [, rawEndIdx] = searchSegment(data.startQs, endTime); + const endIdx = rawEndIdx === -1 ? data.startQs.length : rawEndIdx; for (let i = startIdx; i < endIdx; i++) { - const tStart = Time.fromRaw(data.starts[i]); - let tEnd = Time.fromRaw(data.ends[i]); + const tStart = Time.fromRaw(data.startQs[i]); + let tEnd = Time.fromRaw(data.endQs[i]); const utid = data.utids[i]; // If the last slice is incomplete, it should end with the end of the @@ -320,8 +317,8 @@ class CpuSliceTrack implements Track { if (selection !== null && selection.kind === 'SLICE') { const [startIndex, endIndex] = searchEq(data.ids, selection.id); if (startIndex !== endIndex) { - const tStart = Time.fromRaw(data.starts[startIndex]); - const tEnd = Time.fromRaw(data.ends[startIndex]); + const tStart = Time.fromRaw(data.startQs[startIndex]); + const tEnd = Time.fromRaw(data.endQs[startIndex]); const utid = data.utids[startIndex]; const color = colorForThread(globals.threads.get(utid)); const rectStart = visibleTimeScale.timeToPx(tStart); @@ -413,9 +410,9 @@ class CpuSliceTrack implements Track { const t = visibleTimeScale.pxToHpTime(pos.x); let hoveredUtid = -1; - for (let i = 0; i < data.starts.length; i++) { - const tStart = Time.fromRaw(data.starts[i]); - const tEnd = Time.fromRaw(data.ends[i]); + for (let i = 0; i < data.startQs.length; i++) { + const tStart = Time.fromRaw(data.startQs[i]); + const tEnd = Time.fromRaw(data.endQs[i]); const utid = data.utids[i]; if (t.gte(tStart) && t.lt(tEnd)) { hoveredUtid = utid; @@ -442,7 +439,7 @@ class CpuSliceTrack implements Track { if (data === undefined) return false; const {visibleTimeScale} = globals.timeline; const time = visibleTimeScale.pxToHpTime(x); - const index = search(data.starts, time.toTime()); + const index = search(data.startQs, time.toTime()); const id = index === -1 ? undefined : data.ids[index]; // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!id || this.utidHoveredInThisTrack === -1) return false; |