better already
This commit is contained in:
@@ -2,6 +2,7 @@ use minifb::{Key, Window, WindowOptions};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use teleprof::instrument;
|
use teleprof::instrument;
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ const WIDTH: usize = 800;
|
|||||||
const HEIGHT: usize = 600;
|
const HEIGHT: usize = 600;
|
||||||
const BALL_RADIUS: usize = 20;
|
const BALL_RADIUS: usize = 20;
|
||||||
|
|
||||||
|
static COLOR_PICKER_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
struct Ball {
|
struct Ball {
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
@@ -94,8 +97,9 @@ fn main_frame(
|
|||||||
// If we hit a wall, spawn a thread to pick a new color
|
// If we hit a wall, spawn a thread to pick a new color
|
||||||
if hit_wall {
|
if hit_wall {
|
||||||
let ball_clone = Arc::clone(ball);
|
let ball_clone = Arc::clone(ball);
|
||||||
|
let id = COLOR_PICKER_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
teleprof::set_thread_name("ColorPicker");
|
teleprof::set_thread_name(format!("ColorPicker-{}", id));
|
||||||
pick_new_color(ball_clone);
|
pick_new_color(ball_clone);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ fn main() {
|
|||||||
// Start the telemetry window
|
// Start the telemetry window
|
||||||
teleprof::start();
|
teleprof::start();
|
||||||
|
|
||||||
|
// Name the main thread
|
||||||
|
teleprof::set_thread_name("Main");
|
||||||
|
|
||||||
println!("Teleprof demo running...");
|
println!("Teleprof demo running...");
|
||||||
println!("Press Space in the profiler window to pause/unpause");
|
println!("Press Space in the profiler window to pause/unpause");
|
||||||
println!("Mouse wheel to zoom, drag to pan");
|
println!("Mouse wheel to zoom, drag to pan");
|
||||||
@@ -44,6 +47,7 @@ fn physics_update() {
|
|||||||
// Spawn some worker threads
|
// Spawn some worker threads
|
||||||
let handles: Vec<_> = (0..3).map(|i| {
|
let handles: Vec<_> = (0..3).map(|i| {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
teleprof::set_thread_name(format!("Physics-{}", i));
|
||||||
physics_worker(i);
|
physics_worker(i);
|
||||||
})
|
})
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|||||||
@@ -144,15 +144,14 @@ mod window {
|
|||||||
const MAX_EVENTS: usize = 1_000_000;
|
const MAX_EVENTS: usize = 1_000_000;
|
||||||
|
|
||||||
// Monokai palette
|
// Monokai palette
|
||||||
const COLORS: [u32; 8] = [
|
const COLORS: [u32; 7] = [
|
||||||
|
0x75715E, // Gray
|
||||||
0xF92672, // Pink
|
0xF92672, // Pink
|
||||||
0xA6E22E, // Green
|
0xA6E22E, // Green
|
||||||
0xFD971F, // Orange
|
0xFD971F, // Orange
|
||||||
0x66D9EF, // Cyan
|
0x66D9EF, // Cyan
|
||||||
0xAE81FF, // Purple
|
0xAE81FF, // Purple
|
||||||
0xE6DB74, // Yellow
|
0xE6DB74, // Yellow
|
||||||
0xF8F8F2, // White
|
|
||||||
0x75715E, // Gray
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const BG_COLOR: u32 = 0x272822;
|
const BG_COLOR: u32 = 0x272822;
|
||||||
@@ -230,6 +229,10 @@ mod window {
|
|||||||
timeline_time_offset: f64,
|
timeline_time_offset: f64,
|
||||||
timeline_time_scale: f64,
|
timeline_time_scale: f64,
|
||||||
|
|
||||||
|
// Vertical scroll
|
||||||
|
icicle_scroll_y: f32,
|
||||||
|
timeline_scroll_y: f32,
|
||||||
|
|
||||||
// Mouse state
|
// Mouse state
|
||||||
mouse_down: bool,
|
mouse_down: bool,
|
||||||
last_mouse_x: f32,
|
last_mouse_x: f32,
|
||||||
@@ -238,11 +241,26 @@ mod window {
|
|||||||
mouse_x: f32,
|
mouse_x: f32,
|
||||||
mouse_y: f32,
|
mouse_y: f32,
|
||||||
|
|
||||||
|
// Box selection for zoom
|
||||||
|
selecting: bool,
|
||||||
|
selection_start_x: f32,
|
||||||
|
selection_end_x: f32,
|
||||||
|
|
||||||
|
// Hover state
|
||||||
|
hovered_span: Option<HoveredSpan>,
|
||||||
|
|
||||||
// Pause state
|
// Pause state
|
||||||
paused: bool,
|
paused: bool,
|
||||||
pause_guard: Option<std::sync::MutexGuard<'static, ()>>,
|
pause_guard: Option<std::sync::MutexGuard<'static, ()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct HoveredSpan {
|
||||||
|
name: &'static str,
|
||||||
|
duration_us: f64,
|
||||||
|
thread_id: u64,
|
||||||
|
start_time: f64,
|
||||||
|
}
|
||||||
|
|
||||||
impl ViewState {
|
impl ViewState {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -250,12 +268,18 @@ mod window {
|
|||||||
icicle_time_scale: 100.0,
|
icicle_time_scale: 100.0,
|
||||||
timeline_time_offset: 0.0,
|
timeline_time_offset: 0.0,
|
||||||
timeline_time_scale: 100.0,
|
timeline_time_scale: 100.0,
|
||||||
|
icicle_scroll_y: 0.0,
|
||||||
|
timeline_scroll_y: 0.0,
|
||||||
mouse_down: false,
|
mouse_down: false,
|
||||||
last_mouse_x: 0.0,
|
last_mouse_x: 0.0,
|
||||||
last_mouse_y: 0.0,
|
last_mouse_y: 0.0,
|
||||||
mouse_visible: false,
|
mouse_visible: false,
|
||||||
mouse_x: 0.0,
|
mouse_x: 0.0,
|
||||||
mouse_y: 0.0,
|
mouse_y: 0.0,
|
||||||
|
selecting: false,
|
||||||
|
selection_start_x: 0.0,
|
||||||
|
selection_end_x: 0.0,
|
||||||
|
hovered_span: None,
|
||||||
paused: false,
|
paused: false,
|
||||||
pause_guard: None,
|
pause_guard: None,
|
||||||
}
|
}
|
||||||
@@ -291,8 +315,16 @@ mod window {
|
|||||||
|
|
||||||
let (width, height) = window.get_size();
|
let (width, height) = window.get_size();
|
||||||
|
|
||||||
|
// Collect spans for this frame
|
||||||
|
let spans: Vec<_> = buffer.iter().collect();
|
||||||
|
let earliest = if !spans.is_empty() {
|
||||||
|
spans.iter().map(|s| s.start).min().unwrap()
|
||||||
|
} else {
|
||||||
|
Instant::now()
|
||||||
|
};
|
||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
handle_input(&mut window, &mut view, width, height);
|
handle_input(&mut window, &mut view, &buffer, earliest, width, height);
|
||||||
|
|
||||||
// Resize framebuffer if needed
|
// Resize framebuffer if needed
|
||||||
if framebuffer.len() != width * height {
|
if framebuffer.len() != width * height {
|
||||||
@@ -310,7 +342,7 @@ mod window {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(window: &mut Window, view: &mut ViewState, _width: usize, height: usize) {
|
fn handle_input(window: &mut Window, view: &mut ViewState, buffer: &RingBuffer, earliest: Instant, _width: usize, height: usize) {
|
||||||
// Pause/unpause
|
// Pause/unpause
|
||||||
if window.is_key_pressed(Key::Space, minifb::KeyRepeat::No) {
|
if window.is_key_pressed(Key::Space, minifb::KeyRepeat::No) {
|
||||||
view.paused = !view.paused;
|
view.paused = !view.paused;
|
||||||
@@ -328,47 +360,128 @@ mod window {
|
|||||||
view.mouse_y = my;
|
view.mouse_y = my;
|
||||||
|
|
||||||
let icicle_height = height / 2;
|
let icicle_height = height / 2;
|
||||||
let _is_icicle = my < icicle_height as f32;
|
let is_icicle = my < icicle_height as f32;
|
||||||
|
|
||||||
// Handle mouse wheel (zoom)
|
// Check if shift is pressed (for timeline zoom)
|
||||||
|
let shift_pressed = window.is_key_down(Key::LeftShift) || window.is_key_down(Key::RightShift);
|
||||||
|
|
||||||
|
// Handle mouse wheel
|
||||||
if let Some((_, scroll_y)) = window.get_scroll_wheel() {
|
if let Some((_, scroll_y)) = window.get_scroll_wheel() {
|
||||||
if scroll_y != 0.0 {
|
if scroll_y != 0.0 {
|
||||||
let zoom_factor = if scroll_y > 0.0 { 1.2 } else { 1.0 / 1.2 };
|
if shift_pressed && !is_icicle {
|
||||||
|
// Shift + scroll in timeline = zoom timeline only
|
||||||
|
let zoom_factor = if scroll_y > 0.0 { 1.2 } else { 1.0 / 1.2 };
|
||||||
|
let old_scale = view.timeline_time_scale;
|
||||||
|
let new_scale = old_scale * zoom_factor;
|
||||||
|
let mouse_time = view.timeline_time_offset + (mx as f64 / old_scale);
|
||||||
|
|
||||||
// Synchronize both views - zoom changes scale for both
|
view.timeline_time_scale = new_scale;
|
||||||
let old_scale = view.icicle_time_scale;
|
view.timeline_time_offset = mouse_time - (mx as f64 / new_scale);
|
||||||
let new_scale = old_scale * zoom_factor;
|
|
||||||
|
|
||||||
// Calculate mouse time in world space (same for both views since synchronized)
|
// Also update icicle to match
|
||||||
let mouse_time = view.icicle_time_offset + (mx as f64 / old_scale);
|
view.icicle_time_scale = new_scale;
|
||||||
|
view.icicle_time_offset = view.timeline_time_offset;
|
||||||
|
} else if !shift_pressed && scroll_y.abs() > 0.5 {
|
||||||
|
// Regular scroll = zoom both views (horizontal)
|
||||||
|
let zoom_factor = if scroll_y > 0.0 { 1.2 } else { 1.0 / 1.2 };
|
||||||
|
let old_scale = view.icicle_time_scale;
|
||||||
|
let new_scale = old_scale * zoom_factor;
|
||||||
|
let mouse_time = view.icicle_time_offset + (mx as f64 / old_scale);
|
||||||
|
|
||||||
// Update scale and adjust offset to keep mouse position stable
|
view.icicle_time_scale = new_scale;
|
||||||
view.icicle_time_scale = new_scale;
|
view.timeline_time_scale = new_scale;
|
||||||
view.timeline_time_scale = new_scale;
|
|
||||||
|
|
||||||
let new_offset = mouse_time - (mx as f64 / new_scale);
|
let new_offset = mouse_time - (mx as f64 / new_scale);
|
||||||
view.icicle_time_offset = new_offset;
|
view.icicle_time_offset = new_offset;
|
||||||
view.timeline_time_offset = new_offset;
|
view.timeline_time_offset = new_offset;
|
||||||
|
} else {
|
||||||
|
// Vertical scroll
|
||||||
|
let scroll_amount = scroll_y * 20.0;
|
||||||
|
if is_icicle {
|
||||||
|
view.icicle_scroll_y = (view.icicle_scroll_y - scroll_amount).max(0.0);
|
||||||
|
} else {
|
||||||
|
view.timeline_scroll_y = (view.timeline_scroll_y - scroll_amount).max(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle mouse drag (pan)
|
// Handle mouse buttons
|
||||||
let mouse_down = window.get_mouse_down(MouseButton::Left);
|
let left_down = window.get_mouse_down(MouseButton::Left);
|
||||||
|
let right_down = window.get_mouse_down(MouseButton::Right);
|
||||||
|
|
||||||
if mouse_down && view.mouse_down {
|
// Right click = pan
|
||||||
|
if right_down && view.mouse_down {
|
||||||
let dx = mx - view.last_mouse_x;
|
let dx = mx - view.last_mouse_x;
|
||||||
|
|
||||||
// Pan both views together (synchronized)
|
|
||||||
let delta = dx as f64 / view.icicle_time_scale;
|
let delta = dx as f64 / view.icicle_time_scale;
|
||||||
view.icicle_time_offset -= delta;
|
view.icicle_time_offset -= delta;
|
||||||
view.timeline_time_offset -= delta;
|
view.timeline_time_offset -= delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.mouse_down = mouse_down;
|
// Left click = box selection for zoom
|
||||||
|
if left_down && !view.selecting && !view.mouse_down {
|
||||||
|
view.selecting = true;
|
||||||
|
view.selection_start_x = mx;
|
||||||
|
view.selection_end_x = mx;
|
||||||
|
} else if left_down && view.selecting {
|
||||||
|
view.selection_end_x = mx;
|
||||||
|
} else if !left_down && view.selecting {
|
||||||
|
// Complete selection - zoom to selected region
|
||||||
|
let x1 = view.selection_start_x.min(view.selection_end_x);
|
||||||
|
let x2 = view.selection_start_x.max(view.selection_end_x);
|
||||||
|
|
||||||
|
if (x2 - x1) > 5.0 { // Minimum selection size
|
||||||
|
let time1 = view.icicle_time_offset + (x1 as f64 / view.icicle_time_scale);
|
||||||
|
let time2 = view.icicle_time_offset + (x2 as f64 / view.icicle_time_scale);
|
||||||
|
let time_range = time2 - time1;
|
||||||
|
|
||||||
|
// Calculate new scale to fit selection in view
|
||||||
|
let new_scale = window.get_size().0 as f64 / time_range;
|
||||||
|
view.icicle_time_scale = new_scale;
|
||||||
|
view.timeline_time_scale = new_scale;
|
||||||
|
view.icicle_time_offset = time1;
|
||||||
|
view.timeline_time_offset = time1;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.selecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hover detection
|
||||||
|
update_hover(view, buffer, earliest, mx, my, icicle_height);
|
||||||
|
|
||||||
|
view.mouse_down = left_down || right_down;
|
||||||
view.last_mouse_x = mx;
|
view.last_mouse_x = mx;
|
||||||
view.last_mouse_y = my;
|
view.last_mouse_y = my;
|
||||||
} else {
|
} else {
|
||||||
view.mouse_visible = false;
|
view.mouse_visible = false;
|
||||||
|
view.hovered_span = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_hover(view: &mut ViewState, buffer: &RingBuffer, earliest: Instant, mx: f32, my: f32, icicle_height: usize) {
|
||||||
|
view.hovered_span = None;
|
||||||
|
|
||||||
|
if my >= icicle_height as f32 {
|
||||||
|
return; // Only hover in icicle view for now
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouse_time = view.icicle_time_offset + (mx as f64 / view.icicle_time_scale);
|
||||||
|
|
||||||
|
// Find span under cursor
|
||||||
|
for span in buffer.iter() {
|
||||||
|
let start_time = (span.start - earliest).as_secs_f64();
|
||||||
|
let end_time = (span.end - earliest).as_secs_f64();
|
||||||
|
|
||||||
|
if mouse_time >= start_time && mouse_time <= end_time {
|
||||||
|
let duration_us = (end_time - start_time) * 1_000_000.0;
|
||||||
|
view.hovered_span = Some(HoveredSpan {
|
||||||
|
name: span.name,
|
||||||
|
duration_us,
|
||||||
|
thread_id: span.thread_id,
|
||||||
|
start_time,
|
||||||
|
});
|
||||||
|
break; // Take first match (could be improved to find best match by y-position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,31 +525,189 @@ mod window {
|
|||||||
font,
|
font,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Draw cursor
|
// Draw timestamp axis at the bottom of icicle view
|
||||||
if view.mouse_visible {
|
draw_timestamp_axis(framebuffer, width, icicle_height - 20, view, earliest, font);
|
||||||
let cursor_x = view.mouse_x as usize;
|
|
||||||
|
// Draw selection box if selecting
|
||||||
|
if view.selecting {
|
||||||
|
let x1 = view.selection_start_x.min(view.selection_end_x) as usize;
|
||||||
|
let x2 = view.selection_start_x.max(view.selection_end_x) as usize;
|
||||||
|
let selection_color = 0x4080FF; // Semi-transparent blue
|
||||||
|
|
||||||
// Draw vertical line
|
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
if cursor_x < width {
|
for x in x1..x2.min(width) {
|
||||||
let idx = y * width + cursor_x;
|
let idx = y * width + x;
|
||||||
if idx < framebuffer.len() {
|
if idx < framebuffer.len() {
|
||||||
// Make cursor semi-transparent by blending
|
|
||||||
let bg = framebuffer[idx];
|
let bg = framebuffer[idx];
|
||||||
let bg_r = ((bg >> 16) & 0xFF) as u32;
|
let bg_r = ((bg >> 16) & 0xFF) as f32;
|
||||||
let bg_g = ((bg >> 8) & 0xFF) as u32;
|
let bg_g = ((bg >> 8) & 0xFF) as f32;
|
||||||
let bg_b = (bg & 0xFF) as u32;
|
let bg_b = (bg & 0xFF) as f32;
|
||||||
|
|
||||||
let alpha = 0.5;
|
let sel_r = ((selection_color >> 16) & 0xFF) as f32;
|
||||||
let r = (bg_r as f32 * (1.0 - alpha) + 255.0 * alpha) as u32;
|
let sel_g = ((selection_color >> 8) & 0xFF) as f32;
|
||||||
let g = (bg_g as f32 * (1.0 - alpha) + 255.0 * alpha) as u32;
|
let sel_b = (selection_color & 0xFF) as f32;
|
||||||
let b = (bg_b as f32 * (1.0 - alpha) + 255.0 * alpha) as u32;
|
|
||||||
|
let alpha = 0.3;
|
||||||
|
let r = (bg_r * (1.0 - alpha) + sel_r * alpha) as u32;
|
||||||
|
let g = (bg_g * (1.0 - alpha) + sel_g * alpha) as u32;
|
||||||
|
let b = (bg_b * (1.0 - alpha) + sel_b * alpha) as u32;
|
||||||
|
|
||||||
framebuffer[idx] = (r << 16) | (g << 8) | b;
|
framebuffer[idx] = (r << 16) | (g << 8) | b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw crosshair cursor
|
||||||
|
if view.mouse_visible && !view.selecting {
|
||||||
|
let cursor_x = view.mouse_x as usize;
|
||||||
|
let cursor_y = view.mouse_y as usize;
|
||||||
|
|
||||||
|
// Vertical line
|
||||||
|
for y in 0..height {
|
||||||
|
if cursor_x < width {
|
||||||
|
let idx = y * width + cursor_x;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
framebuffer[idx] = blend_cursor(framebuffer[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal line
|
||||||
|
if cursor_y < height {
|
||||||
|
for x in 0..width {
|
||||||
|
let idx = cursor_y * width + x;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
framebuffer[idx] = blend_cursor(framebuffer[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw hover tooltip
|
||||||
|
if let Some(ref hover) = view.hovered_span {
|
||||||
|
draw_tooltip(framebuffer, width, height, view, hover, font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blend_cursor(bg: u32) -> u32 {
|
||||||
|
let bg_r = ((bg >> 16) & 0xFF) as f32;
|
||||||
|
let bg_g = ((bg >> 8) & 0xFF) as f32;
|
||||||
|
let bg_b = (bg & 0xFF) as f32;
|
||||||
|
|
||||||
|
let alpha = 0.6;
|
||||||
|
let r = (bg_r * (1.0 - alpha) + 255.0 * alpha) as u32;
|
||||||
|
let g = (bg_g * (1.0 - alpha) + 255.0 * alpha) as u32;
|
||||||
|
let b = (bg_b * (1.0 - alpha) + 255.0 * alpha) as u32;
|
||||||
|
|
||||||
|
(r << 16) | (g << 8) | b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_tooltip(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
view: &ViewState,
|
||||||
|
hover: &HoveredSpan,
|
||||||
|
font: &fontdue::Font,
|
||||||
|
) {
|
||||||
|
let tooltip_w = 280;
|
||||||
|
let tooltip_h = 80;
|
||||||
|
let padding = 8;
|
||||||
|
|
||||||
|
// Position tooltip near cursor but keep it on screen
|
||||||
|
let tooltip_x = (view.mouse_x as usize + 20).min(width.saturating_sub(tooltip_w + 10));
|
||||||
|
let tooltip_y = (view.mouse_y as usize + 20).min(height.saturating_sub(tooltip_h + 10));
|
||||||
|
|
||||||
|
// Draw tooltip background
|
||||||
|
for dy in 0..tooltip_h {
|
||||||
|
for dx in 0..tooltip_w {
|
||||||
|
let x = tooltip_x + dx;
|
||||||
|
let y = tooltip_y + dy;
|
||||||
|
if x < width && y < height {
|
||||||
|
let idx = y * width + x;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
// Dark background with border
|
||||||
|
let color = if dx == 0 || dy == 0 || dx == tooltip_w - 1 || dy == tooltip_h - 1 {
|
||||||
|
0x808080 // Border
|
||||||
|
} else {
|
||||||
|
0x1E1E1E // Background
|
||||||
|
};
|
||||||
|
framebuffer[idx] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
let text_x = tooltip_x + padding;
|
||||||
|
let text_y = tooltip_y + padding;
|
||||||
|
|
||||||
|
draw_text(framebuffer, width, text_x, text_y, hover.name, font, 14.0, 0xFFFFFF);
|
||||||
|
draw_text(framebuffer, width, text_x, text_y + 20,
|
||||||
|
&format!("Duration: {:.2} μs", hover.duration_us), font, 12.0, 0xCCCCCC);
|
||||||
|
draw_text(framebuffer, width, text_x, text_y + 38,
|
||||||
|
&format!("Thread: {}", hover.thread_id), font, 12.0, 0xCCCCCC);
|
||||||
|
draw_text(framebuffer, width, text_x, text_y + 56,
|
||||||
|
&format!("Start: {:.6} s", hover.start_time), font, 12.0, 0xCCCCCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_timestamp_axis(
|
||||||
|
framebuffer: &mut [u32],
|
||||||
|
width: usize,
|
||||||
|
y: usize,
|
||||||
|
view: &ViewState,
|
||||||
|
_earliest: Instant,
|
||||||
|
font: &fontdue::Font,
|
||||||
|
) {
|
||||||
|
// Draw background bar
|
||||||
|
for x in 0..width {
|
||||||
|
let idx = y * width + x;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
framebuffer[idx] = 0x1E1E1E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate time markers
|
||||||
|
let visible_duration = width as f64 / view.icicle_time_scale;
|
||||||
|
let time_step = calculate_time_step(visible_duration);
|
||||||
|
|
||||||
|
let start_time = view.icicle_time_offset;
|
||||||
|
let first_marker = (start_time / time_step).ceil() * time_step;
|
||||||
|
|
||||||
|
let mut time = first_marker;
|
||||||
|
while time < start_time + visible_duration {
|
||||||
|
let x = ((time - view.icicle_time_offset) * view.icicle_time_scale) as i32;
|
||||||
|
|
||||||
|
if x >= 0 && x < width as i32 {
|
||||||
|
// Draw tick mark
|
||||||
|
for dy in 0..6 {
|
||||||
|
let idx = (y + dy) * width + x as usize;
|
||||||
|
if idx < framebuffer.len() {
|
||||||
|
framebuffer[idx] = 0x808080;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw time label
|
||||||
|
let label = format!("{:.3}s", time);
|
||||||
|
draw_text(framebuffer, width, (x + 2).max(0) as usize, y + 6, &label, font, 10.0, 0xCCCCCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
time += time_step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_time_step(visible_duration: f64) -> f64 {
|
||||||
|
// Choose appropriate time step based on visible duration
|
||||||
|
let steps = [0.001, 0.002, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0];
|
||||||
|
|
||||||
|
for &step in &steps {
|
||||||
|
if visible_duration / step < 20.0 {
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_icicle(
|
fn render_icicle(
|
||||||
@@ -460,23 +731,32 @@ mod window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort roots by start time
|
||||||
|
roots.sort_by_key(|s| s.start);
|
||||||
|
|
||||||
// Render each root and its children recursively
|
// Render each root and its children recursively
|
||||||
let row_height = 24;
|
let row_height = 24;
|
||||||
let mut y_offset = 0;
|
let mut y_offset = view.icicle_scroll_y as usize;
|
||||||
|
|
||||||
for root in roots {
|
for root in roots {
|
||||||
y_offset = render_icicle_span(
|
// Only render if in view
|
||||||
framebuffer,
|
if y_offset < height {
|
||||||
width,
|
y_offset = render_icicle_span(
|
||||||
height,
|
framebuffer,
|
||||||
root,
|
width,
|
||||||
&children,
|
height,
|
||||||
earliest,
|
root,
|
||||||
y_offset,
|
&children,
|
||||||
row_height,
|
earliest,
|
||||||
view,
|
y_offset,
|
||||||
font,
|
row_height,
|
||||||
);
|
view,
|
||||||
|
font,
|
||||||
|
0, // depth
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,6 +771,7 @@ mod window {
|
|||||||
row_height: usize,
|
row_height: usize,
|
||||||
view: &ViewState,
|
view: &ViewState,
|
||||||
font: &fontdue::Font,
|
font: &fontdue::Font,
|
||||||
|
depth: usize,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
if y + row_height > height {
|
if y + row_height > height {
|
||||||
return y;
|
return y;
|
||||||
@@ -506,36 +787,43 @@ mod window {
|
|||||||
// Only render if visible
|
// Only render if visible
|
||||||
if x2 > 0 && x1 < width as i32 {
|
if x2 > 0 && x1 < width as i32 {
|
||||||
let color = get_color_for_name(span.name);
|
let color = get_color_for_name(span.name);
|
||||||
|
let bar_width = (x2 - x1).max(1) as usize;
|
||||||
|
|
||||||
fill_rect(framebuffer, width, x1.max(0) as usize, y, (x2 - x1).max(1) as usize, row_height - 2, color);
|
fill_rect(framebuffer, width, x1.max(0) as usize, y, bar_width, row_height - 2, color);
|
||||||
|
|
||||||
// Render text if there's enough space
|
// Render text if there's enough space, otherwise render to the right
|
||||||
let text_width = (x2 - x1) as usize;
|
if bar_width > 40 {
|
||||||
if text_width > 40 {
|
|
||||||
draw_text(framebuffer, width, x1.max(0) as usize + 4, y + 4, span.name, font, 14.0, TEXT_COLOR);
|
draw_text(framebuffer, width, x1.max(0) as usize + 4, y + 4, span.name, font, 14.0, TEXT_COLOR);
|
||||||
|
} else if x2 >= 0 && x2 < width as i32 {
|
||||||
|
// Draw text to the right of the bar
|
||||||
|
draw_text(framebuffer, width, x2.max(0) as usize + 4, y + 4, span.name, font, 14.0, TEXT_COLOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render children
|
// Render children at next depth level
|
||||||
let mut next_y = y + row_height;
|
let mut child_y = y + row_height;
|
||||||
if let Some(child_spans) = children.get(&span.span_id) {
|
if let Some(child_spans) = children.get(&span.span_id) {
|
||||||
for child in child_spans {
|
for child in child_spans {
|
||||||
next_y = render_icicle_span(
|
if child_y >= height {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
child_y = render_icicle_span(
|
||||||
framebuffer,
|
framebuffer,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
child,
|
child,
|
||||||
children,
|
children,
|
||||||
earliest,
|
earliest,
|
||||||
next_y,
|
child_y,
|
||||||
row_height,
|
row_height,
|
||||||
view,
|
view,
|
||||||
font,
|
font,
|
||||||
|
depth + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next_y
|
child_y
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_timeline(
|
fn render_timeline(
|
||||||
|
|||||||
Reference in New Issue
Block a user