Skip to content

Commit

Permalink
[Design] CPU Slices Visual Update (#121)
Browse files Browse the repository at this point in the history
* Cpu Slice new Style

* Implement Smaller CPU Slice color palette from designs

* Refine lightness changes based on state

* Change Hover logic to single out a specific slice

* Use Selection for likecase

* Likecase add topbar
  • Loading branch information
ALevansSamsung authored Oct 21, 2024
1 parent 72c9ee9 commit 1504168
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 84 deletions.
5 changes: 5 additions & 0 deletions ui/src/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1337,6 +1337,11 @@ export const StateActions = {
state.hoveredUtid = args.utid;
},

setSelectedUtidAndPid(state: StateDraft, args: {utid: number, pid: number}) {
state.selectedPid = args.pid;
state.selectedUtid = args.utid;
},

setHighlightedSliceId(state: StateDraft, args: {sliceId: number}) {
state.highlightedSliceId = args.sliceId;
},
Expand Down
25 changes: 7 additions & 18 deletions ui/src/common/colorizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,13 @@ export interface Color {
}

const MD_PALETTE: Color[] = [
{c: 'red', h: 4, s: 90, l: 58},
{c: 'pink', h: 340, s: 82, l: 52},
{c: 'purple', h: 291, s: 64, l: 42},
{c: 'deep purple', h: 262, s: 52, l: 47},
{c: 'indigo', h: 231, s: 48, l: 48},
{c: 'blue', h: 207, s: 90, l: 54},
{c: 'light blue', h: 199, s: 98, l: 48},
{c: 'cyan', h: 187, s: 100, l: 42},
{c: 'teal', h: 174, s: 100, l: 29},
{c: 'green', h: 122, s: 39, l: 49},
{c: 'light green', h: 88, s: 50, l: 53},
{c: 'lime', h: 66, s: 70, l: 54},
{c: 'amber', h: 45, s: 100, l: 51},
{c: 'orange', h: 36, s: 100, l: 50},
{c: 'deep orange', h: 14, s: 100, l: 57},
{c: 'brown', h: 16, s: 25, l: 38},
{c: 'blue gray', h: 200, s: 18, l: 46},
{c: 'yellow', h: 54, s: 100, l: 62},
{c: 'teal', h: 173, s: 43, l: 70},
{c: 'purple', h: 267, s: 72, l: 70},
{c: 'peach', h: 23, s: 79, l: 70},
{c: 'red', h: 4, s: 70, l: 70},
{c: 'yellow', h: 46, s: 88, l: 70},
{c: 'green', h: 125, s: 40, l: 70},
{c: 'blue', h: 220, s: 91, l: 70},
];

export const GRAY_COLOR: Color = {
Expand Down
2 changes: 2 additions & 0 deletions ui/src/common/empty_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ export function createEmptyState(): State {
sidebarVisible: true,
hoveredUtid: -1,
hoveredPid: -1,
selectedUtid: -1,
selectedPid: -1,
hoverCursorTimestamp: -1n,
hoveredNoteTimestamp: -1n,
highlightedSliceId: -1,
Expand Down
2 changes: 2 additions & 0 deletions ui/src/common/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ export interface State {
// Hovered and focused events
hoveredUtid: number;
hoveredPid: number;
selectedUtid: number;
selectedPid: number;
hoverCursorTimestamp: TPTime;
hoveredNoteTimestamp: TPTime;
highlightedSliceId: number;
Expand Down
130 changes: 64 additions & 66 deletions ui/src/tracks/cpu_slices/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
drawDoubleHeadedArrow,
drawIncompleteSlice,
} from '../../common/canvas_utils';
import {colorForThread} from '../../common/colorizer';
import {GRAY_COLOR, colorForThread} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
import {LONG, NUM, NUM_NULL} from '../../common/query_result';
import {
Expand All @@ -37,6 +37,7 @@ import {
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {NewTrackArgs, Track} from '../../frontend/track';
import {hash} from '../../common/hash';

export const CPU_SLICE_TRACK_KIND = 'CpuSliceTrack';

Expand Down Expand Up @@ -205,17 +206,16 @@ class CpuSliceTrackController extends TrackController<Config, Data> {
}
}

const MARGIN_TOP = 3;
const RECT_HEIGHT = 24;
const MARGIN_TOP = 1;
const RECT_HEIGHT = 28;

class CpuSliceTrack extends Track<Config, Data> {
static readonly kind = CPU_SLICE_TRACK_KIND;
static create(args: NewTrackArgs): CpuSliceTrack {
return new CpuSliceTrack(args);
}

private mousePos?: {x: number, y: number};
private utidHoveredInThisTrack = -1;
private hoveredSlice: number | undefined;

constructor(args: NewTrackArgs) {
super(args);
Expand Down Expand Up @@ -293,31 +293,58 @@ class CpuSliceTrack extends Track<Config, Data> {
const threadInfo = globals.threads.get(utid);
const pid = threadInfo && threadInfo.pid ? threadInfo.pid : -1;

const isHovering = globals.state.hoveredUtid !== -1;
const isThreadHovered = globals.state.hoveredUtid === utid;
const isProcessHovered = globals.state.hoveredPid === pid;
const isThreadSelected = globals.state.selectedUtid === utid;
const isProcessSelected = globals.state.selectedPid === pid;
const color = colorForThread(threadInfo);
if (isHovering && !isThreadHovered) {
if (!isProcessHovered) {
color.l = 90;
color.s = 0;
} else {
color.l = Math.min(color.l + 30, 80);
color.s -= 20;
}
} else {
color.l = Math.min(color.l + 10, 60);
color.s -= 20;
const greyIdx = hash(pid.toString(), 6)+1;
const greyl = 55 - (5 * greyIdx);
const isHovered = (index: number): boolean =>{
return index === this.hoveredSlice;
};
const isSelected = (): boolean=>{
const selection = globals.state.currentSelection;
return selection !== null && selection.kind === 'SLICE' &&
data.ids[i] ===selection.id;
};

ctx.strokeStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
ctx.fillStyle = `hsl(${GRAY_COLOR.h}, ${GRAY_COLOR.s}%, ${greyl}%)`;
if (isSelected()) {
color.l = 60;
ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
} else if ((isProcessSelected || isThreadSelected) &&
globals.state.currentSelection !== null &&
globals.state.currentSelection.kind === 'SLICE'
) {
// LikeCase
ctx.fillStyle = `hsl(${color.h}, ${color.s}%, 30%)`;
}
ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;

const right = Math.min(visWindowEndPx, rectEnd);
const left = Math.max(rectStart, 0);
const visibleWidth = Math.max(right - left, 1);
// Draw the Rectangle
if (data.isIncomplete[i]) {
drawIncompleteSlice(ctx, rectStart, MARGIN_TOP, rectWidth,
drawIncompleteSlice(ctx, left, MARGIN_TOP, visibleWidth,
(RECT_HEIGHT * this.trackState.scaleFactor));
} else {
ctx.fillRect(rectStart, MARGIN_TOP, rectWidth,
ctx.fillRect(left, MARGIN_TOP, visibleWidth,
(RECT_HEIGHT * this.trackState.scaleFactor));
}

// Extras
if (isHovered(i)) {
// Draw a rectangle around the slice that is currently selected.
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(left+1.5, MARGIN_TOP + 1.5, visibleWidth-3,
(RECT_HEIGHT * this.trackState.scaleFactor) -3);
ctx.closePath();
} else {
ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
ctx.fillRect(left, MARGIN_TOP, visibleWidth,
(3));
}
// Don't render text when we have less than 5px to play with.
if (rectWidth < 5) continue;

Expand All @@ -337,9 +364,6 @@ class CpuSliceTrack extends Track<Config, Data> {
title = `${threadInfo.threadName} [${threadInfo.tid}]`;
}
}
const right = Math.min(visWindowEndPx, rectEnd);
const left = Math.max(rectStart, 0);
const visibleWidth = Math.max(right - left, 1);

const rectXCenter = left + visibleWidth / 2;
ctx.fillStyle = '#fff';
Expand All @@ -366,20 +390,7 @@ class CpuSliceTrack extends Track<Config, Data> {
const [startIndex, endIndex] = searchEq(data.ids, selection.id);
if (startIndex !== endIndex) {
const tStart = data.starts[startIndex];
const tEnd = data.ends[startIndex];
const utid = data.utids[startIndex];
const color = colorForThread(globals.threads.get(utid));
const rectStart = visibleTimeScale.tpTimeToPx(tStart);
const rectEnd = visibleTimeScale.tpTimeToPx(tEnd);
const rectWidth = Math.max(1, rectEnd - rectStart);

// Draw a rectangle around the slice that is currently selected.
ctx.strokeStyle = `hsl(${color.h}, ${color.s}%, 30%)`;
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(rectStart, MARGIN_TOP - 1.5, rectWidth,
(RECT_HEIGHT * this.trackState.scaleFactor) + 3);
ctx.closePath();
// Draw arrow from wakeup time of current slice.
if (details.wakeupTs) {
const wakeupPos = visibleTimeScale.tpTimeToPx(details.wakeupTs);
Expand Down Expand Up @@ -434,63 +445,50 @@ class CpuSliceTrack extends Track<Config, Data> {
ctx.closePath();
}
}

const hoveredThread = globals.threads.get(this.utidHoveredInThisTrack);
if (hoveredThread !== undefined && this.mousePos !== undefined) {
const tidText = `T: ${hoveredThread.threadName} [${hoveredThread.tid}]`;
if (hoveredThread.pid) {
const pidText = `P: ${hoveredThread.procName} [${hoveredThread.pid}]`;
this.drawTrackHoverTooltip(ctx, this.mousePos, pidText, tidText);
} else {
this.drawTrackHoverTooltip(ctx, this.mousePos, tidText);
}
}
}

onMouseMove(pos: {x: number, y: number}) {
const data = this.data();
this.mousePos = pos;
if (data === undefined) return;
const {visibleTimeScale} = globals.frontendLocalState;
if (pos.y < MARGIN_TOP ||
pos.y > MARGIN_TOP + (RECT_HEIGHT * this.trackState.scaleFactor)) {
this.utidHoveredInThisTrack = -1;
globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
return;
}
const t = visibleTimeScale.pxToHpTime(pos.x);
let hoveredUtid = -1;

this.hoveredSlice=undefined;
for (let i = 0; i < data.starts.length; i++) {
const tStart = data.starts[i];
const tEnd = data.ends[i];
const utid = data.utids[i];
if (t.gte(tStart) && t.lt(tEnd)) {
hoveredUtid = utid;
this.hoveredSlice =i;
break;
}
}
this.utidHoveredInThisTrack = hoveredUtid;
const threadInfo = globals.threads.get(hoveredUtid);
const hoveredPid = threadInfo ? (threadInfo.pid ? threadInfo.pid : -1) : -1;
globals.dispatch(
Actions.setHoveredUtidAndPid({utid: hoveredUtid, pid: hoveredPid}));
}

onMouseOut() {
this.utidHoveredInThisTrack = -1;
globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
this.mousePos = undefined;
this.hoveredSlice = undefined;
}

onMouseClick({x}: {x: number}) {
const data = this.data();
if (data === undefined) return false;
if (data === undefined) {
globals.dispatch(Actions.setSelectedUtidAndPid({utid: -1, pid: -1}));
return false;
}
const {visibleTimeScale} = globals.frontendLocalState;
const time = visibleTimeScale.pxToHpTime(x);
const index = search(data.starts, time.toTPTime());
const id = index === -1 ? undefined : data.ids[index];
if (!id || this.utidHoveredInThisTrack === -1) return false;
if (!id) {
globals.dispatch(Actions.setSelectedUtidAndPid({utid: -1, pid: -1}));
return false;
}
const utid = data.utids[index];
const threadInfo = globals.threads.get(utid);
const pid = threadInfo ? (threadInfo.pid ? threadInfo.pid : -1) : -1;
globals.dispatch(Actions.setSelectedUtidAndPid({utid, pid}));
globals.makeSelection(
Actions.selectSlice({id, trackId: this.trackState.id}));
return true;
Expand Down

0 comments on commit 1504168

Please sign in to comment.