From 50ff46deefb1d66deb6c049a30df91e9f9121b2b Mon Sep 17 00:00:00 2001 From: Dipal Date: Tue, 21 Jul 2026 15:45:32 -0400 Subject: [PATCH 1/5] View: Add roofline kernel-intensity memory-peak filter Add a "Kernel intensity" dropdown (All / HBM / L2 / L1 / LDS) to the roofline Options menu so kernel intensity dots can be limited to a single memory level. This declutters the plot versus showing every level's dot per kernel, making it easier to see which roof limits the kernels. The filter is applied consistently to the plotted dots, the legend/options list, and hover hit-testing. Defaults to All (unchanged behavior). Co-authored-by: Cursor --- .../compute/rocprofvis_compute_roofline.cpp | 48 +++++++++++++++++-- .../src/compute/rocprofvis_compute_roofline.h | 7 +++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 2c1af1afb..282c18a5a 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -70,6 +70,7 @@ Roofline::Roofline(DataProvider& data_provider, KernelMode kernel_mode) , m_menus_placement(InsideTopRight) , m_scale_intensity(true) , m_line_thickness(LINE_THICKNESS_DEFAULT) +, m_memory_peak_filter(std::nullopt) , m_menus_rendered_height(0.0f) , m_hovered_item_distance(FLT_MAX) , m_workload_changed(false) @@ -436,7 +437,8 @@ Roofline::Render() { display = m_items[i].info.intensity && - (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true); + (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && + IntensityMatchesMemoryFilter(m_items[i]); } } display &= m_items[i].visible; @@ -787,7 +789,7 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, ImGui::EndGroup(); float header_height = ImGui::GetItemRectSize().y + 2 * style.WindowPadding.y; float footer_height = - (m_kernel_mode == AllKernels ? 6 : 5) * ImGui::GetFrameHeightWithSpacing() + + (m_kernel_mode == AllKernels ? 8 : 7) * ImGui::GetFrameHeightWithSpacing() + 2 * style.WindowPadding.y; ImGui::SetNextWindowSizeConstraints( ImVec2(menus_content_width, 0), @@ -820,7 +822,8 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, { display = m_items[i].info.intensity && - (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true); + (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && + IntensityMatchesMemoryFilter(m_items[i]); break; } } @@ -912,6 +915,32 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, if(m_menus_mode == Options) { ImGui::SeparatorText("Options"); + ImGui::PushID("memory_peak"); + ElidedText("Kernel intensity", ImGui::GetContentRegionAvail().x, + plot_size.x * 0.5f, Alignment_Left, true); + ImGui::SetNextItemWidth(-1.0f); + int intensity_filter_idx = + m_memory_peak_filter + ? static_cast(m_memory_peak_filter.value()) + 1 + : 0; + PushComboStyles(); + // Order must match the intensity enum: All, HBM, L2, L1, LDS. + if(ImGui::Combo("##memory_peak", &intensity_filter_idx, + "All\0HBM\0L2\0L1\0LDS\0\0")) + { + if(intensity_filter_idx == 0) + { + m_memory_peak_filter = std::nullopt; + } + else + { + m_memory_peak_filter = static_cast< + rocprofvis_controller_roofline_kernel_intensity_type_t>( + intensity_filter_idx - 1); + } + } + PopComboStyles(); + ImGui::PopID(); if(m_kernel_mode == AllKernels) { ImGui::PushID("kernel_scale"); @@ -969,7 +998,7 @@ Roofline::PlotHoverIdx() // Pick the closest visible item after plotting all candidates. for(size_t i = 0; i < m_items.size(); i++) { - if(m_items[i].visible) + if(m_items[i].visible && IntensityMatchesMemoryFilter(m_items[i])) { switch(m_items[i].type) { @@ -1059,5 +1088,16 @@ Roofline::ApplyPreset(PresetModel::Type type) m_options_changed = true; } +bool +Roofline::IntensityMatchesMemoryFilter(const ItemModel& item) const +{ + bool matches = true; + if(item.type == ItemModel::Type::Intensity && m_memory_peak_filter) + { + matches = item.subtype.intensity == m_memory_peak_filter.value(); + } + return matches; +} + } // namespace View } // namespace RocProfVis diff --git a/src/view/src/compute/rocprofvis_compute_roofline.h b/src/view/src/compute/rocprofvis_compute_roofline.h index 688dc061e..350c1d0f6 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.h +++ b/src/view/src/compute/rocprofvis_compute_roofline.h @@ -101,6 +101,10 @@ class Roofline : public RocWidget bool& item_hovered); void PlotHoverIdx(); void ApplyPreset(PresetModel::Type type); + // Kernel intensity dots exist per memory level; this decides whether a + // given item survives the active memory-peak filter (non-intensity items + // always pass). + bool IntensityMatchesMemoryFilter(const ItemModel& item) const; // Internal models... std::vector m_items; @@ -112,6 +116,9 @@ class Roofline : public RocWidget MenusPlacement m_menus_placement; bool m_scale_intensity; float m_line_thickness; + // Which memory level's kernel intensity dots to show. nullopt = all levels. + std::optional + m_memory_peak_filter; // Internal state... bool m_workload_changed; From 1b122f265d06af152ef9797ae7a9fbf757983bcf Mon Sep 17 00:00:00 2001 From: Dipal Date: Tue, 21 Jul 2026 16:11:23 -0400 Subject: [PATCH 2/5] View: Isolate roofline kernel by clicking its dot or legend row Add single-click kernel isolation to the workload (all-kernels) roofline: clicking a kernel's dot in the plot or its row in the legend shows only that kernel's intensity dots; clicking it again restores the whole workload. This makes it easy to switch between whole-workload and single-kernel analysis. The dot click works in any zoom state (with a drag check so panning a zoomed plot is not treated as a click) and stacks with the memory-peak filter. Isolation resets when the workload changes. Co-authored-by: Cursor --- .../compute/rocprofvis_compute_roofline.cpp | 58 ++++++++++++++++--- .../src/compute/rocprofvis_compute_roofline.h | 7 +++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 282c18a5a..69e4dca41 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -82,6 +82,7 @@ Roofline::Roofline(DataProvider& data_provider, KernelMode kernel_mode) , m_requested_workload_id(0) , m_kernel(nullptr) , m_requested_kernel_id(0) +, m_isolated_kernel(nullptr) { m_widget_name = GenUniqueName("roofline"); m_items.resize(static_cast(__KRPVControllerRooflineCeilingComputeTypeLast + @@ -121,6 +122,8 @@ Roofline::Update() { if(m_workload_changed) { + // Kernel pointers are rebuilt below, so drop any stale isolation. + m_isolated_kernel = nullptr; m_workload = m_data_provider.ComputeModel().GetWorkload(m_requested_workload_id); if(m_workload) @@ -438,7 +441,8 @@ Roofline::Render() display = m_items[i].info.intensity && (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && - IntensityMatchesMemoryFilter(m_items[i]); + IntensityMatchesMemoryFilter(m_items[i]) && + KernelPassesIsolation(m_items[i]); } } display &= m_items[i].visible; @@ -604,8 +608,18 @@ Roofline::Render() } bool menus_item_hovered = false; RenderMenus(region, plot_pos, plot_size, style, plot_style, menus_item_hovered); - if(!m_plot_zoom_enabled && roofline_hovered && - ImGui::IsMouseClicked(ImGuiMouseButton_Left)) + bool dot_hovered = + !menus_item_hovered && m_kernel_mode == AllKernels && m_hovered_item_idx && + m_items[m_hovered_item_idx.value()].type == ItemModel::Type::Intensity; + // Drag check so panning a zoomed plot is not treated as a dot click. + if(dot_hovered && roofline_hovered && + IsMouseReleasedWithDragCheck(ImGuiMouseButton_Left)) + { + ToggleKernelIsolation( + m_items[m_hovered_item_idx.value()].parent_info.kernel); + } + if(!m_plot_zoom_enabled && roofline_hovered && !dot_hovered && + !menus_item_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { m_plot_zoom_enabled = true; } @@ -823,7 +837,8 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, display = m_items[i].info.intensity && (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && - IntensityMatchesMemoryFilter(m_items[i]); + IntensityMatchesMemoryFilter(m_items[i]) && + KernelPassesIsolation(m_items[i]); break; } } @@ -896,10 +911,18 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, { m_hovered_item_idx = i; item_hovered = true; - if(row_clicked && m_menus_mode == Options) + if(row_clicked) { - m_items[i].visible = !m_items[i].visible; - m_options_changed = true; + if(m_menus_mode == Options) + { + m_items[i].visible = !m_items[i].visible; + m_options_changed = true; + } + else if(m_kernel_mode == AllKernels && + m_items[i].type == ItemModel::Type::Intensity) + { + ToggleKernelIsolation(m_items[i].parent_info.kernel); + } } } ImGui::PopStyleColor(3); @@ -998,7 +1021,8 @@ Roofline::PlotHoverIdx() // Pick the closest visible item after plotting all candidates. for(size_t i = 0; i < m_items.size(); i++) { - if(m_items[i].visible && IntensityMatchesMemoryFilter(m_items[i])) + if(m_items[i].visible && IntensityMatchesMemoryFilter(m_items[i]) && + KernelPassesIsolation(m_items[i])) { switch(m_items[i].type) { @@ -1099,5 +1123,23 @@ Roofline::IntensityMatchesMemoryFilter(const ItemModel& item) const return matches; } +void +Roofline::ToggleKernelIsolation(const KernelInfo* kernel) +{ + m_isolated_kernel = (kernel && m_isolated_kernel != kernel) ? kernel : nullptr; +} + +bool +Roofline::KernelPassesIsolation(const ItemModel& item) const +{ + bool passes = true; + if(m_kernel_mode == AllKernels && m_isolated_kernel && + item.type == ItemModel::Type::Intensity) + { + passes = item.parent_info.kernel == m_isolated_kernel; + } + return passes; +} + } // namespace View } // namespace RocProfVis diff --git a/src/view/src/compute/rocprofvis_compute_roofline.h b/src/view/src/compute/rocprofvis_compute_roofline.h index 350c1d0f6..3123e2cf3 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.h +++ b/src/view/src/compute/rocprofvis_compute_roofline.h @@ -105,6 +105,12 @@ class Roofline : public RocWidget // given item survives the active memory-peak filter (non-intensity items // always pass). bool IntensityMatchesMemoryFilter(const ItemModel& item) const; + // Isolate a single kernel's dots in the workload roofline. Clicking the + // same kernel again clears isolation and restores the whole workload. + void ToggleKernelIsolation(const KernelInfo* kernel); + // Whether an item survives the active kernel isolation (non-intensity + // items, and everything outside AllKernels mode, always pass). + bool KernelPassesIsolation(const ItemModel& item) const; // Internal models... std::vector m_items; @@ -128,6 +134,7 @@ class Roofline : public RocWidget bool m_kernel_changed; const KernelInfo* m_kernel; uint32_t m_requested_kernel_id; + const KernelInfo* m_isolated_kernel; bool m_options_changed; bool m_plot_zoom_enabled; std::optional m_hovered_item_idx; From 45579aaa8b6a1f2d585fd153c0e7cae4ec4980e8 Mon Sep 17 00:00:00 2001 From: Dipal Date: Tue, 21 Jul 2026 16:24:27 -0400 Subject: [PATCH 3/5] View: Isolate roofline bandwidth peak from legend or plot line Add single-click bandwidth-peak isolation to the roofline: clicking a bandwidth ceiling row in the legend, or its line in the plot, shows only that peak (HBM/L2/L1/LDS) and hides the others. Clicking it again restores all peaks. The legend keeps all bandwidth rows and highlights the active one, so peaks can be switched with a single click; the plot line toggles the shown peak (a drag check keeps a zoomed-plot pan from counting as a click). Compute ceilings and kernel dots are unaffected, and this stacks with the memory-peak filter and kernel isolation. Isolation resets on workload change. Co-authored-by: Cursor --- .../compute/rocprofvis_compute_roofline.cpp | 72 +++++++++++++++---- .../src/compute/rocprofvis_compute_roofline.h | 5 ++ 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 69e4dca41..578f6fb58 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -83,6 +83,7 @@ Roofline::Roofline(DataProvider& data_provider, KernelMode kernel_mode) , m_kernel(nullptr) , m_requested_kernel_id(0) , m_isolated_kernel(nullptr) +, m_isolated_bandwidth(std::nullopt) { m_widget_name = GenUniqueName("roofline"); m_items.resize(static_cast(__KRPVControllerRooflineCeilingComputeTypeLast + @@ -123,7 +124,8 @@ Roofline::Update() if(m_workload_changed) { // Kernel pointers are rebuilt below, so drop any stale isolation. - m_isolated_kernel = nullptr; + m_isolated_kernel = nullptr; + m_isolated_bandwidth = std::nullopt; m_workload = m_data_provider.ComputeModel().GetWorkload(m_requested_workload_id); if(m_workload) @@ -433,7 +435,8 @@ Roofline::Render() case ItemModel::Type::CeilingCompute: case ItemModel::Type::CeilingBandwidth: { - display = m_items[i].info.ceiling; + display = m_items[i].info.ceiling && + BandwidthPassesIsolation(m_items[i]); break; } case ItemModel::Type::Intensity: @@ -611,15 +614,27 @@ Roofline::Render() bool dot_hovered = !menus_item_hovered && m_kernel_mode == AllKernels && m_hovered_item_idx && m_items[m_hovered_item_idx.value()].type == ItemModel::Type::Intensity; - // Drag check so panning a zoomed plot is not treated as a dot click. - if(dot_hovered && roofline_hovered && - IsMouseReleasedWithDragCheck(ImGuiMouseButton_Left)) + bool bandwidth_line_hovered = + !menus_item_hovered && m_hovered_item_idx && + m_items[m_hovered_item_idx.value()].type == + ItemModel::Type::CeilingBandwidth; + // Drag check so panning a zoomed plot is not treated as a click. + if(roofline_hovered && IsMouseReleasedWithDragCheck(ImGuiMouseButton_Left)) { - ToggleKernelIsolation( - m_items[m_hovered_item_idx.value()].parent_info.kernel); + if(dot_hovered) + { + ToggleKernelIsolation( + m_items[m_hovered_item_idx.value()].parent_info.kernel); + } + else if(bandwidth_line_hovered) + { + ToggleBandwidthIsolation( + m_items[m_hovered_item_idx.value()].subtype.bandwidth); + } } if(!m_plot_zoom_enabled && roofline_hovered && !dot_hovered && - !menus_item_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) + !bandwidth_line_hovered && !menus_item_hovered && + ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { m_plot_zoom_enabled = true; } @@ -853,9 +868,13 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, m_settings.GetColor(Colors::kHighlightChart)); ImGui::PushStyleColor(ImGuiCol_HeaderActive, m_settings.GetColor(Colors::kHighlightChart)); - ImVec2 pos = ImGui::GetCursorPos(); - bool row_clicked = ImGui::Selectable( - "", false, + ImVec2 pos = ImGui::GetCursorPos(); + bool row_isolated = m_isolated_bandwidth && + m_items[i].type == ItemModel::Type::CeilingBandwidth && + m_items[i].subtype.bandwidth == + m_isolated_bandwidth.value(); + bool row_clicked = ImGui::Selectable( + "", row_isolated, m_hovered_item_idx && m_hovered_item_idx.value() == i ? ImGuiSelectableFlags_Highlight : ImGuiSelectableFlags_None); @@ -923,6 +942,10 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, { ToggleKernelIsolation(m_items[i].parent_info.kernel); } + else if(m_items[i].type == ItemModel::Type::CeilingBandwidth) + { + ToggleBandwidthIsolation(m_items[i].subtype.bandwidth); + } } } ImGui::PopStyleColor(3); @@ -1022,7 +1045,7 @@ Roofline::PlotHoverIdx() for(size_t i = 0; i < m_items.size(); i++) { if(m_items[i].visible && IntensityMatchesMemoryFilter(m_items[i]) && - KernelPassesIsolation(m_items[i])) + KernelPassesIsolation(m_items[i]) && BandwidthPassesIsolation(m_items[i])) { switch(m_items[i].type) { @@ -1141,5 +1164,30 @@ Roofline::KernelPassesIsolation(const ItemModel& item) const return passes; } +void +Roofline::ToggleBandwidthIsolation( + rocprofvis_controller_roofline_ceiling_bandwidth_type_t bandwidth) +{ + if(m_isolated_bandwidth && m_isolated_bandwidth.value() == bandwidth) + { + m_isolated_bandwidth = std::nullopt; + } + else + { + m_isolated_bandwidth = bandwidth; + } +} + +bool +Roofline::BandwidthPassesIsolation(const ItemModel& item) const +{ + bool passes = true; + if(m_isolated_bandwidth && item.type == ItemModel::Type::CeilingBandwidth) + { + passes = item.subtype.bandwidth == m_isolated_bandwidth.value(); + } + return passes; +} + } // namespace View } // namespace RocProfVis diff --git a/src/view/src/compute/rocprofvis_compute_roofline.h b/src/view/src/compute/rocprofvis_compute_roofline.h index 3123e2cf3..0bec70f25 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.h +++ b/src/view/src/compute/rocprofvis_compute_roofline.h @@ -111,6 +111,9 @@ class Roofline : public RocWidget // Whether an item survives the active kernel isolation (non-intensity // items, and everything outside AllKernels mode, always pass). bool KernelPassesIsolation(const ItemModel& item) const; + void ToggleBandwidthIsolation( + rocprofvis_controller_roofline_ceiling_bandwidth_type_t bandwidth); + bool BandwidthPassesIsolation(const ItemModel& item) const; // Internal models... std::vector m_items; @@ -135,6 +138,8 @@ class Roofline : public RocWidget const KernelInfo* m_kernel; uint32_t m_requested_kernel_id; const KernelInfo* m_isolated_kernel; + std::optional + m_isolated_bandwidth; bool m_options_changed; bool m_plot_zoom_enabled; std::optional m_hovered_item_idx; From c1e8ec1ec17558453a93f2bce5f1164c51f6d337 Mon Sep 17 00:00:00 2001 From: Dipal Date: Thu, 23 Jul 2026 12:43:05 -0400 Subject: [PATCH 4/5] View: Rework roofline filters into dropdowns and drive them via visibility Address roofline PR review feedback: - Replace the preset buttons and scattered filter controls with dropdowns under the Presets header: Preset, Kernel intensity, Kernel, and Bandwidth peak. The dot/line/legend click shortcuts still complement them. - Drive all filtering through each item's visibility (like ApplyPreset) instead of separate display predicates, so the Options menu always lists the full data set; only the chart, legend, and hover react to filters. - Recompute visibility sets m_options_changed so ceiling changes re-fit the ridges (fixes bandwidth-peak discontinuities). - Offer only the intensity/bandwidth levels actually present in the workload. - Elide long kernel names in the Kernel dropdown so its popup stays put. Co-authored-by: Cursor --- .../compute/rocprofvis_compute_roofline.cpp | 320 ++++++++++++------ .../src/compute/rocprofvis_compute_roofline.h | 32 +- 2 files changed, 227 insertions(+), 125 deletions(-) diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 578f6fb58..3c161e64c 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -51,6 +51,8 @@ constexpr const char* DISPLAY_NAMES_KERNEL_INTENSITY[] = { "L1 Intensity", // kRPVControllerRooflineKernelIntensityTypeL1 "LDS Intensity", // kRPVControllerRooflineKernelIntensityTypeLDS }; +constexpr const char* MEMORY_LEVEL_NAMES[] = { "HBM", "L2", "L1", "LDS" }; +constexpr const char* FILTER_OPTION_ALL = "All"; constexpr const char* DISPLAY_NAMES_PRESET[] = { "FP4", // PresetModel::Type::FP4 "FP6", // PresetModel::Type::FP6 @@ -70,6 +72,7 @@ Roofline::Roofline(DataProvider& data_provider, KernelMode kernel_mode) , m_menus_placement(InsideTopRight) , m_scale_intensity(true) , m_line_thickness(LINE_THICKNESS_DEFAULT) +, m_active_preset(PresetModel::FP32) , m_memory_peak_filter(std::nullopt) , m_menus_rendered_height(0.0f) , m_hovered_item_distance(FLT_MAX) @@ -123,9 +126,10 @@ Roofline::Update() { if(m_workload_changed) { - // Kernel pointers are rebuilt below, so drop any stale isolation. + // Kernel pointers are rebuilt below, so drop any stale filters. m_isolated_kernel = nullptr; m_isolated_bandwidth = std::nullopt; + m_memory_peak_filter = std::nullopt; m_workload = m_data_provider.ComputeModel().GetWorkload(m_requested_workload_id); if(m_workload) @@ -259,6 +263,41 @@ Roofline::Update() static_cast(kernel_duration_scale)) }); } } + // Build filter dropdown options from what the workload actually has; + // empty memory levels should not be offered. + m_available_intensities.clear(); + m_available_bandwidths.clear(); + bool intensity_present[IM_ARRAYSIZE(MEMORY_LEVEL_NAMES)] = {}; + for(const std::pair& kernel : m_workload->kernels) + { + for(const std::pair< + const rocprofvis_controller_roofline_kernel_intensity_type_t, + KernelInfo::Roofline::Intensity>& intensity : + kernel.second.roofline.intensities) + { + intensity_present[intensity.second.type] = true; + } + } + for(uint32_t i = 0; i < IM_ARRAYSIZE(MEMORY_LEVEL_NAMES); i++) + { + if(intensity_present[i]) + { + m_available_intensities.emplace_back( + static_cast< + rocprofvis_controller_roofline_kernel_intensity_type_t>(i)); + } + } + for(uint32_t i = __KRPVControllerRooflineCeilingBandwidthTypeFirst; + i < __KRPVControllerRooflineCeilingBandwidthTypeLast; i++) + { + rocprofvis_controller_roofline_ceiling_bandwidth_type_t bandwidth = + static_cast( + i); + if(m_workload->roofline.ceiling_bandwidth.count(bandwidth) > 0) + { + m_available_bandwidths.emplace_back(bandwidth); + } + } ApplyPreset(PresetModel::FP32); } m_workload_changed = false; @@ -270,6 +309,7 @@ Roofline::Update() { m_kernel = &m_workload->kernels.at(m_requested_kernel_id); } + RecomputeVisibility(); m_kernel_changed = false; } if(m_options_changed) @@ -435,17 +475,12 @@ Roofline::Render() case ItemModel::Type::CeilingCompute: case ItemModel::Type::CeilingBandwidth: { - display = m_items[i].info.ceiling && - BandwidthPassesIsolation(m_items[i]); + display = m_items[i].info.ceiling; break; } case ItemModel::Type::Intensity: { - display = - m_items[i].info.intensity && - (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && - IntensityMatchesMemoryFilter(m_items[i]) && - KernelPassesIsolation(m_items[i]); + display = m_items[i].info.intensity; } } display &= m_items[i].visible; @@ -802,28 +837,141 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, if(m_menus_mode == Options) { ImGui::SeparatorText("Presets"); - for(PresetModel& preset : m_presets) + ImGui::PushID("preset"); + ElidedText("Preset", ImGui::GetContentRegionAvail().x, plot_size.x * 0.5f, + Alignment_Left, true); + ImGui::SetNextItemWidth(-1.0f); + PushComboStyles(); + if(ImGui::BeginCombo("##preset", DISPLAY_NAMES_PRESET[m_active_preset])) { - if(!preset.item_indices.empty()) + for(PresetModel& preset : m_presets) { - if(ImGui::Button(DISPLAY_NAMES_PRESET[preset.type], - ImVec2(-1.0f, 0.0f))) + if(!preset.item_indices.empty() && + ImGui::Selectable(DISPLAY_NAMES_PRESET[preset.type], + preset.type == m_active_preset)) { ApplyPreset(preset.type); } } + ImGui::EndCombo(); + } + PopComboStyles(); + ImGui::PopID(); + if(!m_available_intensities.empty()) + { + ImGui::PushID("kernel_intensity"); + ElidedText("Kernel intensity", ImGui::GetContentRegionAvail().x, + plot_size.x * 0.5f, Alignment_Left, true); + ImGui::SetNextItemWidth(-1.0f); + PushComboStyles(); + if(ImGui::BeginCombo("##kernel_intensity", + m_memory_peak_filter + ? MEMORY_LEVEL_NAMES[m_memory_peak_filter.value()] + : FILTER_OPTION_ALL)) + { + if(ImGui::Selectable(FILTER_OPTION_ALL, !m_memory_peak_filter)) + { + m_memory_peak_filter = std::nullopt; + RecomputeVisibility(); + } + for(rocprofvis_controller_roofline_kernel_intensity_type_t level : + m_available_intensities) + { + if(ImGui::Selectable(MEMORY_LEVEL_NAMES[level], + m_memory_peak_filter && + m_memory_peak_filter.value() == level)) + { + m_memory_peak_filter = level; + RecomputeVisibility(); + } + } + ImGui::EndCombo(); + } + PopComboStyles(); + ImGui::PopID(); + } + if(m_kernel_mode == AllKernels && m_workload) + { + ImGui::PushID("kernel"); + ElidedText("Kernel", ImGui::GetContentRegionAvail().x, plot_size.x * 0.5f, + Alignment_Left, true); + ImGui::SetNextItemWidth(-1.0f); + PushComboStyles(); + if(ImGui::BeginCombo("##kernel", + m_isolated_kernel ? m_isolated_kernel->name.c_str() + : FILTER_OPTION_ALL)) + { + if(ImGui::Selectable(FILTER_OPTION_ALL, !m_isolated_kernel)) + { + m_isolated_kernel = nullptr; + RecomputeVisibility(); + } + for(const KernelInfo* kernel : m_workload->ordered_kernels) + { + // Kernel names are long/mangled; elide them so the combo + // popup stays the width of the menu. + ImGui::PushID(kernel); + ImVec2 pos = ImGui::GetCursorPos(); + bool clicked = ImGui::Selectable("", m_isolated_kernel == kernel); + ImGui::SetCursorPos(pos); + ElidedText(kernel->name.c_str(), + ImGui::GetContentRegionAvail().x, plot_size.x * 0.5f); + if(clicked) + { + m_isolated_kernel = kernel; + RecomputeVisibility(); + } + ImGui::PopID(); + } + ImGui::EndCombo(); + } + PopComboStyles(); + ImGui::PopID(); + } + if(!m_available_bandwidths.empty()) + { + ImGui::PushID("bandwidth_peak"); + ElidedText("Bandwidth peak", ImGui::GetContentRegionAvail().x, + plot_size.x * 0.5f, Alignment_Left, true); + ImGui::SetNextItemWidth(-1.0f); + PushComboStyles(); + if(ImGui::BeginCombo("##bandwidth_peak", + m_isolated_bandwidth + ? MEMORY_LEVEL_NAMES[m_isolated_bandwidth.value()] + : FILTER_OPTION_ALL)) + { + if(ImGui::Selectable(FILTER_OPTION_ALL, !m_isolated_bandwidth)) + { + m_isolated_bandwidth = std::nullopt; + RecomputeVisibility(); + } + for(rocprofvis_controller_roofline_ceiling_bandwidth_type_t bandwidth : + m_available_bandwidths) + { + if(ImGui::Selectable(MEMORY_LEVEL_NAMES[bandwidth], + m_isolated_bandwidth && + m_isolated_bandwidth.value() == bandwidth)) + { + m_isolated_bandwidth = bandwidth; + RecomputeVisibility(); + } + } + ImGui::EndCombo(); + } + PopComboStyles(); + ImGui::PopID(); } ImGui::SeparatorText("Custom"); } ImGui::EndGroup(); float header_height = ImGui::GetItemRectSize().y + 2 * style.WindowPadding.y; float footer_height = - (m_kernel_mode == AllKernels ? 8 : 7) * ImGui::GetFrameHeightWithSpacing() + + (m_kernel_mode == AllKernels ? 6 : 5) * ImGui::GetFrameHeightWithSpacing() + 2 * style.WindowPadding.y; ImGui::SetNextWindowSizeConstraints( ImVec2(menus_content_width, 0), ImVec2(menus_content_width, - max_menus_height - header_height - footer_height)); + std::max(0.0f, max_menus_height - header_height - footer_height))); ImGui::BeginChild("menus_scroll_view", ImVec2(menus_content_width, 0), ImGuiChildFlags_AutoResizeY); float icon_width = ImGui::GetFontSize(); @@ -849,11 +997,12 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, } case ItemModel::Type::Intensity: { - display = - m_items[i].info.intensity && - (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true) && - IntensityMatchesMemoryFilter(m_items[i]) && - KernelPassesIsolation(m_items[i]); + // The menu always lists the full data set; in single-kernel + // mode that data set is just the selected kernel. + display = m_items[i].info.intensity && + (m_kernel_mode == SingleKernel + ? m_items[i].parent_info.kernel == m_kernel + : true); break; } } @@ -868,13 +1017,9 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, m_settings.GetColor(Colors::kHighlightChart)); ImGui::PushStyleColor(ImGuiCol_HeaderActive, m_settings.GetColor(Colors::kHighlightChart)); - ImVec2 pos = ImGui::GetCursorPos(); - bool row_isolated = m_isolated_bandwidth && - m_items[i].type == ItemModel::Type::CeilingBandwidth && - m_items[i].subtype.bandwidth == - m_isolated_bandwidth.value(); - bool row_clicked = ImGui::Selectable( - "", row_isolated, + ImVec2 pos = ImGui::GetCursorPos(); + bool row_clicked = ImGui::Selectable( + "", false, m_hovered_item_idx && m_hovered_item_idx.value() == i ? ImGuiSelectableFlags_Highlight : ImGuiSelectableFlags_None); @@ -961,32 +1106,6 @@ Roofline::RenderMenus(ImVec2 region, ImVec2 plot_pos, ImVec2 plot_size, if(m_menus_mode == Options) { ImGui::SeparatorText("Options"); - ImGui::PushID("memory_peak"); - ElidedText("Kernel intensity", ImGui::GetContentRegionAvail().x, - plot_size.x * 0.5f, Alignment_Left, true); - ImGui::SetNextItemWidth(-1.0f); - int intensity_filter_idx = - m_memory_peak_filter - ? static_cast(m_memory_peak_filter.value()) + 1 - : 0; - PushComboStyles(); - // Order must match the intensity enum: All, HBM, L2, L1, LDS. - if(ImGui::Combo("##memory_peak", &intensity_filter_idx, - "All\0HBM\0L2\0L1\0LDS\0\0")) - { - if(intensity_filter_idx == 0) - { - m_memory_peak_filter = std::nullopt; - } - else - { - m_memory_peak_filter = static_cast< - rocprofvis_controller_roofline_kernel_intensity_type_t>( - intensity_filter_idx - 1); - } - } - PopComboStyles(); - ImGui::PopID(); if(m_kernel_mode == AllKernels) { ImGui::PushID("kernel_scale"); @@ -1044,8 +1163,7 @@ Roofline::PlotHoverIdx() // Pick the closest visible item after plotting all candidates. for(size_t i = 0; i < m_items.size(); i++) { - if(m_items[i].visible && IntensityMatchesMemoryFilter(m_items[i]) && - KernelPassesIsolation(m_items[i]) && BandwidthPassesIsolation(m_items[i])) + if(m_items[i].visible) { switch(m_items[i].type) { @@ -1106,62 +1224,56 @@ Roofline::PlotHoverIdx() void Roofline::ApplyPreset(PresetModel::Type type) { - // Disable all compute cielings... - for(size_t i = 0; - i < static_cast(__KRPVControllerRooflineCeilingComputeTypeLast); i++) + m_active_preset = type; + RecomputeVisibility(); +} + +void +Roofline::RecomputeVisibility() +{ + for(ItemModel& item : m_items) { - m_items[i].visible = false; + switch(item.type) + { + case ItemModel::Type::CeilingCompute: + { + // Enabled below from the active preset. + item.visible = false; + break; + } + case ItemModel::Type::CeilingBandwidth: + { + item.visible = !m_isolated_bandwidth || + item.subtype.bandwidth == m_isolated_bandwidth.value(); + break; + } + case ItemModel::Type::Intensity: + { + bool level_ok = !m_memory_peak_filter || + item.subtype.intensity == m_memory_peak_filter.value(); + bool kernel_ok = + m_kernel_mode == SingleKernel + ? item.parent_info.kernel == m_kernel + : (!m_isolated_kernel || + item.parent_info.kernel == m_isolated_kernel); + item.visible = level_ok && kernel_ok; + break; + } + } } - // Enable preset's compute ceilings... - for(const size_t& item_idx : m_presets[type].item_indices) + for(const size_t& item_idx : m_presets[m_active_preset].item_indices) { m_items[item_idx].visible = true; } - // Enable all bandwidth ceilings... - for(size_t i = static_cast(__KRPVControllerRooflineCeilingComputeTypeLast); - i < static_cast(__KRPVControllerRooflineCeilingComputeTypeLast + - __KRPVControllerRooflineCeilingBandwidthTypeLast); - i++) - { - m_items[i].visible = true; - } - // Enable all kernels... - for(size_t i = static_cast(__KRPVControllerRooflineCeilingComputeTypeLast + - __KRPVControllerRooflineCeilingBandwidthTypeLast); - i < m_items.size(); i++) - { - m_items[i].visible = true; - } + // Visibility can change which ceilings are shown, so recompute the ridges. m_options_changed = true; } -bool -Roofline::IntensityMatchesMemoryFilter(const ItemModel& item) const -{ - bool matches = true; - if(item.type == ItemModel::Type::Intensity && m_memory_peak_filter) - { - matches = item.subtype.intensity == m_memory_peak_filter.value(); - } - return matches; -} - void Roofline::ToggleKernelIsolation(const KernelInfo* kernel) { m_isolated_kernel = (kernel && m_isolated_kernel != kernel) ? kernel : nullptr; -} - -bool -Roofline::KernelPassesIsolation(const ItemModel& item) const -{ - bool passes = true; - if(m_kernel_mode == AllKernels && m_isolated_kernel && - item.type == ItemModel::Type::Intensity) - { - passes = item.parent_info.kernel == m_isolated_kernel; - } - return passes; + RecomputeVisibility(); } void @@ -1176,17 +1288,7 @@ Roofline::ToggleBandwidthIsolation( { m_isolated_bandwidth = bandwidth; } -} - -bool -Roofline::BandwidthPassesIsolation(const ItemModel& item) const -{ - bool passes = true; - if(m_isolated_bandwidth && item.type == ItemModel::Type::CeilingBandwidth) - { - passes = item.subtype.bandwidth == m_isolated_bandwidth.value(); - } - return passes; + RecomputeVisibility(); } } // namespace View diff --git a/src/view/src/compute/rocprofvis_compute_roofline.h b/src/view/src/compute/rocprofvis_compute_roofline.h index 0bec70f25..ed8a6b721 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.h +++ b/src/view/src/compute/rocprofvis_compute_roofline.h @@ -101,31 +101,31 @@ class Roofline : public RocWidget bool& item_hovered); void PlotHoverIdx(); void ApplyPreset(PresetModel::Type type); - // Kernel intensity dots exist per memory level; this decides whether a - // given item survives the active memory-peak filter (non-intensity items - // always pass). - bool IntensityMatchesMemoryFilter(const ItemModel& item) const; - // Isolate a single kernel's dots in the workload roofline. Clicking the - // same kernel again clears isolation and restores the whole workload. + // Recompute every item's visibility from the active preset and the + // intensity/kernel/bandwidth filters. The chart, legend, and hover read + // visibility; the Options menu always lists the full data set. + void RecomputeVisibility(); void ToggleKernelIsolation(const KernelInfo* kernel); - // Whether an item survives the active kernel isolation (non-intensity - // items, and everything outside AllKernels mode, always pass). - bool KernelPassesIsolation(const ItemModel& item) const; void ToggleBandwidthIsolation( rocprofvis_controller_roofline_ceiling_bandwidth_type_t bandwidth); - bool BandwidthPassesIsolation(const ItemModel& item) const; // Internal models... std::vector m_items; std::vector m_presets; + // Filter options actually present in the workload (non-empty only). + std::vector + m_available_intensities; + std::vector + m_available_bandwidths; // User options... - bool m_show_menus; - MenusMode m_menus_mode; - MenusPlacement m_menus_placement; - bool m_scale_intensity; - float m_line_thickness; - // Which memory level's kernel intensity dots to show. nullopt = all levels. + bool m_show_menus; + MenusMode m_menus_mode; + MenusPlacement m_menus_placement; + bool m_scale_intensity; + float m_line_thickness; + PresetModel::Type m_active_preset; + // Selected filters. nullopt = show all of that category. std::optional m_memory_peak_filter; From e94def3cccb0b7ed93d2d6f419c8a0cfea5d66d7 Mon Sep 17 00:00:00 2001 From: Dipal Date: Fri, 24 Jul 2026 15:14:50 -0400 Subject: [PATCH 5/5] View: Move roofline filters into an always-visible top toolbar Address roofline PR review feedback: - Lay the four filter dropdowns out horizontally above the plot, always accessible instead of buried behind the gear button, with inline labels. - Rename/reorder to Compute peak, Bandwidth peak, Kernel, Kernel bandwidth. - Keep only line thickness, menus position, and the scale-marker checkbox in the gear settings panel. - Exclude kernels with no roofline data from the Kernel dropdown. - Revert the scroll-view height clamp so the Custom list no longer clips in short panels. Co-authored-by: Cursor --- .../compute/rocprofvis_compute_roofline.cpp | 306 ++++++++++-------- .../src/compute/rocprofvis_compute_roofline.h | 2 + 2 files changed, 182 insertions(+), 126 deletions(-) diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 3c161e64c..acc964f76 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -379,6 +379,15 @@ Roofline::Render() ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding); SectionTitle("Roofline Analysis"); + bool has_roofline = + m_workload && !m_workload->roofline.ceiling_bandwidth.empty() && + !m_workload->roofline.ceiling_compute.empty() && + !(m_kernel_mode == SingleKernel && + (!m_kernel || m_kernel->roofline.intensities.empty())); + if(has_roofline) + { + RenderToolbar(); + } ImGui::BeginChild("roofline"); const ImVec2 region = ImGui::GetContentRegionAvail(); const ImGuiStyle& style = ImGui::GetStyle(); @@ -707,6 +716,176 @@ Roofline::SetKernel(uint32_t id) m_kernel_changed = true; } +void +Roofline::RenderToolbar() +{ + const ImGuiStyle& style = ImGui::GetStyle(); + int count = 1; // Compute peak is always present. + if(!m_available_bandwidths.empty()) + { + count++; + } + if(m_kernel_mode == AllKernels) + { + count++; + } + if(!m_available_intensities.empty()) + { + count++; + } + float cell = (ImGui::GetContentRegionAvail().x - + style.ItemSpacing.x * static_cast(count - 1)) / + static_cast(count); + cell = std::max(cell, ImGui::GetFontSize() * 4.0f); + + // Draw "