Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 110 additions & 15 deletions src/addons/devkit/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,34 @@ std::string setting_device_proxy_compile_error = {};
bool g_device_proxy_dx9ex_bootstrap_pending = false;
bool g_device_proxy_disable_flip_bootstrap_pending = false;

int pending_draw_index_focus = -1;
// Tracks pending scroll-to-shader navigation in the snapshot pane.
struct SnapshotNavigation {
int target_draw_index = -1;
uint32_t target_shader_hash = 0; // 0 = draw-level nav
int source_model_row = -1; // snapshot_rows index of the row that was clicked
float source_scroll_y = 0.f; // GetScrollY() at click time

void Clear() {
target_draw_index = -1;
target_shader_hash = 0;
source_model_row = -1;
source_scroll_y = 0.f;
}

[[nodiscard]] bool IsPending() const { return target_draw_index >= 0; }
[[nodiscard]] bool HasShaderTarget() const { return target_shader_hash != 0; }

void ScrollToRow(int target_model_row, float row_height) {
if (source_model_row >= 0 && target_model_row >= 0) {
int row_delta = target_model_row - source_model_row;
ImGui::SetScrollY(source_scroll_y + (static_cast<float>(row_delta) * row_height));
} else {
ImGui::SetScrollHereY(0.5f);
}

Clear();
}
} snapshot_nav;

struct SettingSelection {
uint32_t shader_hash = 0;
Expand Down Expand Up @@ -3787,19 +3814,37 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();

const float snapshot_row_height = ImGui::GetFrameHeight();
// Row height must include CellPadding.y on both sides. Using the wrong
// value causes the ImGuiListClipper to underreport total content height,
// clipping the bottom rows and miscalculating scroll-to-row offsets.
const float snapshot_row_height = ImGui::GetFrameHeight() + (ImGui::GetStyle().CellPadding.y * 2.0f);
const uint32_t snapshot_row_layout_key =
(snapshot_pane_show_vertex_shaders ? 1u << 0u : 0u)
| (snapshot_pane_show_pixel_shaders ? 1u << 1u : 0u)
| (snapshot_pane_show_compute_shaders ? 1u << 2u : 0u)
| (snapshot_pane_filter_resources_by_shader_use ? 1u << 3u : 0u)
| (snapshot_pane_expand_all_nodes ? 1u << 4u : 0u);

int current_model_row = -1;

// Called from render_draw_row after the tree node. For draw-level nav,
// scrolls here. For shader-level nav, defers to focus_pending_shader.
const auto focus_pending_draw = [&](int draw_index) {
if (draw_index == pending_draw_index_focus) {
if (draw_index == snapshot_nav.target_draw_index) {
if (snapshot_nav.HasShaderTarget()) return;

ImGui::SetItemDefaultFocus();
ImGui::SetScrollHereY();
pending_draw_index_focus = -1;
snapshot_nav.ScrollToRow(current_model_row, snapshot_row_height);
}
};

// Called from render_shader_row. Matches on draw index + shader hash
// to scroll to the exact shader row, not just the parent draw.
const auto focus_pending_shader = [&](const SnapshotRow& row) {
if (snapshot_nav.HasShaderTarget()
&& row.draw_index == snapshot_nav.target_draw_index
&& row.shader_hash == snapshot_nav.target_shader_hash) {
snapshot_nav.ScrollToRow(current_model_row, snapshot_row_height);
}
};

Expand Down Expand Up @@ -3930,13 +3975,17 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
ImGui::EndDisabled();
};

ImGui::PushID(row.draw_index);
ImGui::PushID(row.id_seed);
render_nav_button("##prev", !has_prev, has_prev ? *prev_it : -1);
render_nav_button("##next", !has_next, has_next ? *next_it : -1);
ImGui::PopID();

if (new_index != -1) {
pending_draw_index_focus = new_index;
snapshot_nav.target_draw_index = new_index;
snapshot_nav.target_shader_hash = row.shader_hash;
snapshot_nav.source_model_row = current_model_row;
snapshot_nav.source_scroll_y = ImGui::GetScrollY();

MakeSelectionCurrent(selection);
}
}
Expand All @@ -3961,7 +4010,7 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
ImGui::TreeNodeEx("", bullet_flags, "%s", s.str().c_str());
}
ImGui::PopID();
if (pending_draw_index_focus == -1) {
if (!snapshot_nav.IsPending()) {
if (ImGui::IsItemClicked()) {
MakeSelectionCurrent(selection);
ImGui::SetItemDefaultFocus();
Expand All @@ -3984,6 +4033,8 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
ImGui::TextUnformatted(pipeline_details_ptr->tag->c_str());
}
}

focus_pending_shader(row);
};

const auto render_srv_row = [&](const SnapshotRow& row, bool& next_tree_open) {
Expand Down Expand Up @@ -4763,6 +4814,7 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
sync_snapshot_tree_stack(ancestor_chain);
ImGui::TableNextRow(ImGuiTableRowFlags_None, snapshot_row_height);
++rendered_row_count;
current_model_row = row_model_index;

bool next_tree_open = false;

Expand Down Expand Up @@ -5170,17 +5222,59 @@ void RenderCapturePane(reshade::api::device* device, DeviceData* data) {
data->snapshot_row_layout_key = snapshot_row_layout_key;
data->snapshot_rows_valid = true;
};

// Shader rows only exist in the model when their parent draw's tree is
// open. Force the target draw open so rebuild_snapshot_rows includes the
// shader children we need to scroll to.
if (snapshot_nav.IsPending() && snapshot_nav.HasShaderTarget()) {
ImGui::PushID(snapshot_nav.target_draw_index);
ImGui::GetStateStorage()->SetInt(ImGui::GetID(""), 1);
ImGui::PopID();
data->snapshot_rows_valid = false;
}
if (!data->snapshot_rows_valid || data->snapshot_row_layout_key != snapshot_row_layout_key) {
rebuild_snapshot_rows();
}
int rendered_row_count = 0;
if (pending_draw_index_focus != -1 || data->snapshot_rows.empty()) {
rendered_row_count = render_capture_pane_rows(
0,
static_cast<int>(data->snapshot_rows.size()));
} else {
{
ImGuiListClipper clipper;
clipper.Begin(static_cast<int>(data->snapshot_rows.size()), snapshot_row_height);
if (snapshot_nav.IsPending()) {
int draw_row_idx = -1;
int shader_row_idx = -1;

for (int i = 0; i < static_cast<int>(data->snapshot_rows.size()); ++i) {
const auto& r = data->snapshot_rows[static_cast<size_t>(i)];

if (draw_row_idx < 0
&& r.kind == SnapshotRow::Kind::DRAW
&& r.draw_index == snapshot_nav.target_draw_index) {
draw_row_idx = i;
}

if (snapshot_nav.HasShaderTarget()
&& r.kind == SnapshotRow::Kind::SHADER
&& r.draw_index == snapshot_nav.target_draw_index
&& r.shader_hash == snapshot_nav.target_shader_hash) {
shader_row_idx = i;
}

if (draw_row_idx >= 0 && (snapshot_nav.target_shader_hash == 0 || shader_row_idx >= 0)) break;
}

if (draw_row_idx < 0) {
snapshot_nav = {};
} else {
// If we searched and the shader row doesn't exist, fall back to
// draw-level scroll.
if (snapshot_nav.HasShaderTarget() && shader_row_idx < 0) {
snapshot_nav.target_shader_hash = 0;
}

clipper.IncludeItemByIndex(draw_row_idx);
if (shader_row_idx >= 0) clipper.IncludeItemByIndex(shader_row_idx);
}
}
while (clipper.Step()) {
rendered_row_count += render_capture_pane_rows(
clipper.DisplayStart,
Expand Down Expand Up @@ -5208,8 +5302,9 @@ inline void CreateDrawIndexLink(const std::string& label, int draw_index, const
ImGui::PushStyleColor(ImGuiCol_Text, *color);
}
if (ImGui::TextLink(label.c_str())) {
setting_nav_item = 0; // Snapshot is the first nav item
pending_draw_index_focus = draw_index;
setting_nav_item = 0;
snapshot_nav.Clear();
snapshot_nav.target_draw_index = draw_index;
}
if (color != nullptr) {
ImGui::PopStyleColor();
Expand Down
Loading