diff --git a/src/cargo/core/compiler/timings.js b/src/cargo/core/compiler/timings.js index d9a0b53bd6c..a38f538d63c 100644 --- a/src/cargo/core/compiler/timings.js +++ b/src/cargo/core/compiler/timings.js @@ -19,9 +19,8 @@ let HIT_BOXES = []; let LAST_HOVER = null; // Key is unit index, value is {x, y, width, rmeta_x} of the box. let UNIT_COORDS = {}; -// Map of unit index to the index it was unlocked by. -let REVERSE_UNIT_DEPS = {}; -let REVERSE_UNIT_RMETA_DEPS = {}; +// Cache of measured text widths for SVG +let MEASURE_TEXT_CACHE = {}; // Colors from css const getCssColor = name => getComputedStyle(document.body).getPropertyValue(name); @@ -37,58 +36,16 @@ const DEP_LINE_COLOR = getCssColor('--canvas-dep-line'); const DEP_LINE_HIGHLIGHTED_COLOR = getCssColor('--canvas-dep-line-highlighted'); const CPU_COLOR = getCssColor('--canvas-cpu'); -for (let n=0; n unit.duration >= min_time); const graph_height = Y_TICK_DIST * units.length; - const {ctx, graph_width, canvas_width, canvas_height, px_per_sec} = draw_graph_axes('pipeline-graph', graph_height); - const container = document.getElementById('pipeline-container'); - container.style.width = canvas_width; - container.style.height = canvas_height; - - // Canvas for hover highlights. This is a separate layer to improve performance. - const linectx = setup_canvas('pipeline-graph-lines', canvas_width, canvas_height); - linectx.clearRect(0, 0, canvas_width, canvas_height); - ctx.strokeStyle = AXES_COLOR; - // Draw Y tick marks. - for (let n=1; n`; + const svg = document.getElementById(`pipeline-graph`); + if (svg) { + svg.style.width = canvas_width; + svg.style.height = canvas_height; + svg.innerHTML = ( + `${axis_bottom}${axis_left}${dep_lines}${boxes}${dep_lines_hl_container}` + ); + let g = document.getElementById('boxes'); + g.onmousemove = pipeline_mousemove; + } +} - const labelName = (unitCount.get(unit.name) || 0) > 1 ? `${unit.name} (v${unit.version})${unit.target}` : `${unit.name}${unit.target}`; +function create_boxes(units, unitCount, canvas_width, px_per_sec) { + let boxes = units.map(unit => { + const { x, y, width, rmeta_x } = UNIT_COORDS[unit.i] + const labelName = + (unitCount.get(unit.name) || 0) > 1 + ? `${unit.name} (v${unit.version})${unit.target}` + : `${unit.name}${unit.target}`; const label = `${labelName}: ${unit.duration}s`; + const textinfo_width = measure_text_width(label); + const label_x = Math.min(x + 5.0, canvas_width - textinfo_width - X_LINE); + const rmeta_rect = unit.rmeta_time ? + `` + : ""; + return ( + ` + ${rmeta_rect} + ${label} + ` + ) + }).join(""); + return `${boxes}` +} - const text_info = ctx.measureText(label); - const label_x = Math.min(x + 5.0, canvas_width - text_info.width - X_LINE); - ctx.fillText(label, label_x, y + BOX_HEIGHT / 2); - draw_dep_lines(ctx, unit.i, false); +function measure_text_width(text) { + if (text in MEASURE_TEXT_CACHE) { + return MEASURE_TEXT_CACHE[text]; } - ctx.restore(); + + let div = document.createElement('DIV'); + div.innerHTML = text; + Object.assign(div.style, { + position: 'absolute', + top: '-100px', + left: '-100px', + fontFamily: 'sans-serif', + fontSize: '14px' + }); + document.body.appendChild(div); + let width = div.offsetWidth; + document.body.removeChild(div); + + MEASURE_TEXT_CACHE[text] = width; + return width; } -// Draws lines from the given unit to the units it unlocks. -function draw_dep_lines(ctx, unit_idx, highlighted) { - const unit = UNIT_DATA[unit_idx]; - const {x, y, rmeta_x} = UNIT_COORDS[unit_idx]; - ctx.save(); - for (const unlocked of unit.unlocked_units) { - draw_one_dep_line(ctx, x, y, unlocked, highlighted); - } - for (const unlocked of unit.unlocked_rmeta_units) { - draw_one_dep_line(ctx, rmeta_x, y, unlocked, highlighted); - } - ctx.restore(); +// Create lines from the given unit to the units it unlocks. +function create_dep_lines(units) { + const lines = units + .filter(unit => unit.i in UNIT_COORDS) + .map(unit => { + const { i, unlocked_units, unlocked_rmeta_units } = unit; + const { x: from_x, y: from_y, rmeta_x } = UNIT_COORDS[i] + let dep_lines = unlocked_units + .filter(unlocked => unlocked in UNIT_COORDS) + .map(unlocked => create_one_dep_line(from_x, from_y, i, unlocked, "dep")) + .join(""); + let rmeta_dep_lines = unlocked_rmeta_units + .filter(unlocked => unlocked in UNIT_COORDS) + .map(unlocked => create_one_dep_line(rmeta_x, from_y, i, unlocked, "rmeta")) + .join(""); + return [dep_lines, rmeta_dep_lines]; + }).flat().join(""); + return `${lines}` } -function draw_one_dep_line(ctx, from_x, from_y, to_unit, highlighted) { - if (to_unit in UNIT_COORDS) { - let {x: u_x, y: u_y} = UNIT_COORDS[to_unit]; - ctx.strokeStyle = highlighted ? DEP_LINE_HIGHLIGHTED_COLOR: DEP_LINE_COLOR; - ctx.setLineDash([2]); - ctx.beginPath(); - ctx.moveTo(from_x, from_y+BOX_HEIGHT/2); - ctx.lineTo(from_x-5, from_y+BOX_HEIGHT/2); - ctx.lineTo(from_x-5, u_y+BOX_HEIGHT/2); - ctx.lineTo(u_x, u_y+BOX_HEIGHT/2); - ctx.stroke(); - } +function create_one_dep_line(from_x, from_y, from_unit, to_unit, dep_type) { + const { x: u_x, y: u_y } = UNIT_COORDS[to_unit]; + const prefix = dep_type == "rmeta" ? "rdep" : "dep"; + return ( + ` + ` + ) } function render_timing_graph() { @@ -316,7 +317,7 @@ function setup_canvas(id, width, height) { return ctx; } -function draw_graph_axes(id, graph_height) { +function resize_graph(graph_height) { const scale = document.getElementById('scale').valueAsNumber; // Cap the size of the graph. It is hard to view if it is too large, and // browsers may not render a large graph because it takes too much memory. @@ -326,6 +327,11 @@ function draw_graph_axes(id, graph_height) { const px_per_sec = graph_width / DURATION; const canvas_width = Math.max(graph_width + X_LINE + 30, X_LINE + 250); const canvas_height = graph_height + MARGIN + Y_LINE; + return { canvas_width, canvas_height, graph_width, graph_height, px_per_sec }; +} + +function draw_graph_axes(id, graph_height) { + let { canvas_width, canvas_height, graph_width, px_per_sec } = resize_graph(graph_height); let ctx = setup_canvas(id, canvas_width, canvas_height); ctx.fillStyle = CANVAS_BG; ctx.fillRect(0, 0, canvas_width, canvas_height); @@ -367,7 +373,54 @@ function draw_graph_axes(id, graph_height) { } ctx.strokeStyle = TEXT_COLOR; ctx.setLineDash([]); - return {canvas_width, canvas_height, graph_width, graph_height, ctx, px_per_sec}; + return { canvas_width, canvas_height, graph_width, graph_height, ctx, px_per_sec }; +} + +function create_axis_bottom({ canvas_height, graph_width, graph_height, px_per_sec }) { + const { step, tick_dist, num_ticks } = split_ticks(DURATION, px_per_sec, graph_width); + const grid_height = canvas_height - Y_LINE - MARGIN; + const ticks = Array(num_ticks).fill(0).map((_, idx) => { + const i = idx + 1; + const time = i * step; + return ( + ` + + + ${time}s + ` + ) + }).join(""); + + const height = graph_height; + const width = graph_width + 20; + return ( + ` + + ${ticks} + ` + ); +} + +function create_axis_left(graph_height, ticks_num) { + const text_offset = -Y_TICK_DIST / 2; + const ticks = Array(ticks_num).fill(0).map((_, idx) => { + const i = idx + 1; + let mark = (i == ticks_num) ? "" : + ``; + return ( + ` + ${mark}${i} + ` + ) + }).join(""); + + const height = graph_height + 1; + return ( + ` + + ${ticks} + ` + ) } // Determine the spacing and number of ticks along an axis. @@ -429,44 +482,33 @@ function roundedRect(ctx, x, y, width, height, r) { } function pipeline_mouse_hit(event) { - // This brute-force method can be optimized if needed. - for (let box of HIT_BOXES) { - if (event.offsetX >= box.x && event.offsetX <= box.x2 && - event.offsetY >= box.y && event.offsetY <= box.y2) { - return box; - } + const target = event.target; + if (target.tagName == 'rect') { + return target.parentNode.dataset.i; } } function pipeline_mousemove(event) { // Highlight dependency lines on mouse hover. - let box = pipeline_mouse_hit(event); - if (box) { - if (box.i != LAST_HOVER) { - LAST_HOVER = box.i; - let g = document.getElementById('pipeline-graph-lines'); - let ctx = g.getContext('2d'); - ctx.clearRect(0, 0, g.width, g.height); - ctx.save(); - ctx.translate(X_LINE, MARGIN); - ctx.lineWidth = 2; - draw_dep_lines(ctx, box.i, true); - - if (box.i in REVERSE_UNIT_DEPS) { - const dep_unit = REVERSE_UNIT_DEPS[box.i]; - if (dep_unit in UNIT_COORDS) { - const {x, y, rmeta_x} = UNIT_COORDS[dep_unit]; - draw_one_dep_line(ctx, x, y, box.i, true); - } - } - if (box.i in REVERSE_UNIT_RMETA_DEPS) { - const dep_unit = REVERSE_UNIT_RMETA_DEPS[box.i]; - if (dep_unit in UNIT_COORDS) { - const {x, y, rmeta_x} = UNIT_COORDS[dep_unit]; - draw_one_dep_line(ctx, rmeta_x, y, box.i, true); - } - } - ctx.restore(); + let i = pipeline_mouse_hit(event); + if (i && i != LAST_HOVER) { + let deps = + document.querySelectorAll(`.dep-line[data-i="${LAST_HOVER}"],#dep-${LAST_HOVER},#rdep-${LAST_HOVER}`); + for (let el of deps) { + el.classList.remove('hl'); + } + + LAST_HOVER = i; + deps = document.querySelectorAll(`.dep-line[data-i="${LAST_HOVER}"],#dep-${LAST_HOVER},#rdep-${LAST_HOVER}`); + let ids = []; + for (let el of deps) { + el.classList.add('hl'); + ids.push(el.id); + } + + let hl = document.getElementById('hl-pipeline'); + if (hl) { + hl.innerHTML = ids.map(id => ``).join(''); } } } diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index ec6e7741e31..2934447d143 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -679,10 +679,63 @@ h1 { border-bottom: 1px solid var(--h1-border-bottom); } +#pipeline-graph { + background-color: var(--canvas-background); +} + .graph { display: block; } +.graph line, .graph polyline { + stroke: currentColor; + stroke-width: 2; +} + +.graph text { + fill: currentColor; + stroke-width: 0; + dominant-baseline: central; +} + +.graph .grid, .dep-line { + stroke-dasharray: 2; +} + +.graph .grid { + color: var(--canvas-grid); +} + +.dep-line { + color: var(--canvas-dep-line); + fill: none; +} + +.dep-line.hl { + color: var(--canvas-dep-line-highlighted); +} + +.axis { + color: var(--canvas-axes); +} + +.box rect { + fill: var(--canvas-not-custom-build); +} + +.box rect.rmeta { + fill: var(--canvas-block); +} + +.box.run-custom-build rect { + fill: var(--canvas-custom-build); +} + +.box text { + font-size: 14px; + pointer-events: none; +} + .my-table { margin-top: 20px; margin-bottom: 20px; @@ -789,10 +842,7 @@ static HTML_CANVAS: &str = r#" -
- - -
+