[Request] Generic Depth addon: "Lowest" heuristics + "Filter by reversed depth"
- Berlint
- Topic Author
Less
More
4 days 7 hours ago - 4 days 7 hours ago #1
by Berlint
[Request] Generic Depth addon: "Lowest" heuristics + "Filter by reversed depth" was created by Berlint
Hi,
I'd like to request two small additions to the built-in Generic Depth addon
(examples/09-depth/generic_depth_addon.cpp), which fixed depth detection for
me on Vulkan (PCSX2). Right now the auto-detect keeps "jumping" between
buffers and there is no way to stabilize it.
Problem:
- On Vulkan (PCSX2, also RPCS3/DXVK titles) the detected depth buffer is
unstable - it flips between frames/clears.
- Same instability also happens on some DX12 titles (and DXVK->Vulkan titles)
where multiple depth buffers compete - the heuristic picks the biggest
one, but the correct scene depth is the smaller workload, or a
reversed-depth buffer that cannot be preferred.
- "Draw stats heuristic" only offers Default / Higher vertices /
Higher draw calls. For emulator UI / small buffers the *smallest*
workload is the right one, but there is no "Lowest" option.
- Many Vulkan titles use reversed depth (clear != 1.0). The overlay shows
a "Reversed" tag in the buffer list, but there is no way to
prefer/exclude reversed buffers in the heuristic.
Proposed additions:
1. Draw stats heuristic: "Lowest vertices" and "Lowest draw calls"
(values 3 and 4, keeping 0-2 stable for existing presets). Selection
(operator>
and copy-at-clear logic inverted to <= for these modes,
with empty-stats handling.
2. New combo "Filter by reversed depth": Any (0, default) /
Prefer reversed (1) / Exclude reversed (2), saved as DEPTH/FilterReversed,
filtering candidates in on_begin_render_effects by
last_frame_stats.reversed_clear_value.
I tested this locally (built ReShade64.dll from main + patch):
- PCSX2 / Vulkan, C:\ProgramData\ReShade\ReShade64\ReShade64.dll replaced.
- Overlay -> Generic Depth -> Draw stats heuristic: Lowest vertices +
Filter by reversed depth: Prefer reversed -> stable, correct depth.
Without it the buffer jumps every few frames.
- Also verified on DX12: copy patched ReShade64.dll renamed to dxgi.dll
next to the game .exe - Lowest + reversed filter stabilizes detection
there too.
Happy to open a PR with the patch (file: generic_depth_addon.cpp, ~50 lines)
if you are interested. I have a working implementation ready.
Thanks for ReShade!
I'd like to request two small additions to the built-in Generic Depth addon
(examples/09-depth/generic_depth_addon.cpp), which fixed depth detection for
me on Vulkan (PCSX2). Right now the auto-detect keeps "jumping" between
buffers and there is no way to stabilize it.
Problem:
- On Vulkan (PCSX2, also RPCS3/DXVK titles) the detected depth buffer is
unstable - it flips between frames/clears.
- Same instability also happens on some DX12 titles (and DXVK->Vulkan titles)
where multiple depth buffers compete - the heuristic picks the biggest
one, but the correct scene depth is the smaller workload, or a
reversed-depth buffer that cannot be preferred.
- "Draw stats heuristic" only offers Default / Higher vertices /
Higher draw calls. For emulator UI / small buffers the *smallest*
workload is the right one, but there is no "Lowest" option.
- Many Vulkan titles use reversed depth (clear != 1.0). The overlay shows
a "Reversed" tag in the buffer list, but there is no way to
prefer/exclude reversed buffers in the heuristic.
Proposed additions:
1. Draw stats heuristic: "Lowest vertices" and "Lowest draw calls"
(values 3 and 4, keeping 0-2 stable for existing presets). Selection
(operator>
with empty-stats handling.
2. New combo "Filter by reversed depth": Any (0, default) /
Prefer reversed (1) / Exclude reversed (2), saved as DEPTH/FilterReversed,
filtering candidates in on_begin_render_effects by
last_frame_stats.reversed_clear_value.
I tested this locally (built ReShade64.dll from main + patch):
- PCSX2 / Vulkan, C:\ProgramData\ReShade\ReShade64\ReShade64.dll replaced.
- Overlay -> Generic Depth -> Draw stats heuristic: Lowest vertices +
Filter by reversed depth: Prefer reversed -> stable, correct depth.
Without it the buffer jumps every few frames.
- Also verified on DX12: copy patched ReShade64.dll renamed to dxgi.dll
next to the game .exe - Lowest + reversed filter stabilizes detection
there too.
Happy to open a PR with the patch (file: generic_depth_addon.cpp, ~50 lines)
if you are interested. I have a working implementation ready.
Thanks for ReShade!
From: berlint-hub
Subject: [PATCH] Generic Depth: lowest heuristics + filter by reversed depth
Date: Fri, 09 Oct 2026
Helps depth detection on Vulkan emulators (PCSX2/RPCS3) and DX12 titles
where the auto-detect keeps jumping between buffers.
Applies against crosire/reshade@main:
examples/09-depth/generic_depth_addon.cpp
---
examples/09-depth/generic_depth_addon.cpp | 53 ++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 4 deletions(-)
diff --git a/examples/09-depth/generic_depth_addon.cpp b/examples/09-depth/generic_depth_addon.cpp
index 0000000..0000001 100644
--- a/examples/09-depth/generic_depth_addon.cpp
+++ b/examples/09-depth/generic_depth_addon.cpp
@@ -22,6 +22,8 @@ enum class draw_stats_heuristic : unsigned int
enum class draw_stats_heuristic : unsigned int
{
prefer_vertices = 0,
vertices,
- drawcalls
+ drawcalls,
+ lowest_vertices,
+ lowest_drawcalls
};
@@ -45,3 +45,5 @@
// Enable or disable the format check from 'check_depth_format' in the detection heuristic
static unsigned int s_format_filtering = 0;
static unsigned int s_custom_resolution_filtering[2] = {};
+// Filter by reversed depth buffer detection (0 = any, 1 = prefer reversed, 2 = exclude reversed)
+static unsigned int s_reversed_filtering = 0;
@@ -62,8 +64,17 @@ struct draw_stats
bool operator>(const draw_stats &other) const
{
+ const bool is_empty = vertices == 0 && drawcalls == 0;
+ const bool other_empty = other.vertices == 0 && other.drawcalls == 0;
+ if (is_empty != other_empty)
+ return other_empty; // Any real stats are better than empty, regardless of heuristic
+
if (s_draw_stats_heuristic == draw_stats_heuristic::vertices)
return vertices > other.vertices;
if (s_draw_stats_heuristic == draw_stats_heuristic::drawcalls)
return drawcalls > other.drawcalls;
+ if (s_draw_stats_heuristic == draw_stats_heuristic::lowest_vertices)
+ return vertices < other.vertices;
+ if (s_draw_stats_heuristic == draw_stats_heuristic::lowest_drawcalls)
+ return drawcalls < other.drawcalls;
return (drawcalls_indirect < (drawcalls / 3) ?
@@ -430,8 +441,17 @@ static void on_clear_depth_impl(command_list *cmd_list, state_
if (s_force_clear_index.first == 0)
{
+ if (s_draw_stats_heuristic == draw_stats_heuristic::lowest_vertices || s_draw_stats_heuristic == draw_stats_heuristic::lowest_drawcalls)
+ {
+ const bool best_empty = state.best_copy_stats.vertices == 0 && state.best_copy_stats.drawcalls == 0;
+ // Inverted logic for lowest heuristics: keep the smallest workload (e.g. for UI / small depth buffers)
+ do_copy = best_empty || current_stats.vertices <= state.best_copy_stats.vertices || (op == clear_op::fullscreen_draw && current_stats.drawcalls <= state.best_copy_stats.drawcalls);
+ }
+ else
+ {
+ // Use greater equals operator here to handle case where the same scene is first rendered into a shadow map and then for real (e.g. Mirror's Edge main menu)
+ do_copy = current_stats.vertices >= state.best_copy_stats.vertices || (op == clear_op::fullscreen_draw && current_stats.drawcalls >= state.best_copy_stats.drawcalls);
+ }
- // Use greater equals operator here to handle case where the same scene is first rendered into a shadow map and then for real (e.g. Mirror's Edge main menu)
- do_copy = current_stats.vertices >= state.best_copy_stats.vertices || (op == clear_op::fullscreen_draw && current_stats.drawcalls >= state.best_copy_stats.drawcalls);
}
@@ -520,6 +540,10 @@ static void on_init_device(device *device)
reshade::get_config_value(nullptr, "DEPTH", "DrawStatsHeuristic", reinterpret_cast<unsigned int &>(s_draw_stats_heuristic));
+ if (s_draw_stats_heuristic > draw_stats_heuristic::lowest_drawcalls)
+ s_draw_stats_heuristic = draw_stats_heuristic::prefer_vertices;
reshade::get_config_value(nullptr, "DEPTH", "UseAspectRatioHeuristics", reinterpret_cast<unsigned int &>(s_aspect_ratio_heuristic));
reshade::get_config_value(nullptr, "DEPTH", "FilterFormat", s_format_filtering);
+ reshade::get_config_value(nullptr, "DEPTH", "FilterReversed", s_reversed_filtering);
reshade::get_config_value(nullptr, "DEPTH", "FilterResolutionWidth", s_custom_resolution_filtering[0]);
reshade::get_config_value(nullptr, "DEPTH", "FilterResolutionHeight", s_custom_resolution_filtering[1]);
@@ -1000,6 +1024,13 @@ static void on_begin_render_effects(effect_runtime *runtime
if (s_aspect_ratio_heuristic != aspect_ratio_heuristic::none && !check_aspect_ratio(static_cast<float>(info.desc.texture.width), static_cast<float>(info.desc.texture.height), static_cast<float>(frame_width), static_cast<float>(frame_height)))
continue; // Not a good fit
+ // Filter by reversed depth buffer detection
+ const bool is_reversed = info.last_frame_stats.reversed_clear_value;
+ if (s_reversed_filtering == 1 && !is_reversed)
+ continue; // Only allow reversed
+ if (s_reversed_filtering == 2 && is_reversed)
+ continue; // Exclude reversed
+
if (selected_depth_stencil.handle == 0 ||
@@ -1215,6 +1246,8 @@ static void draw_settings_overlay(effect_runtime *runtime)
const char *const draw_stats_heuristic_items[] = {
"Default",
"Higher vertices",
- "Higher draw calls"
+ "Higher draw calls",
+ "Lowest vertices",
+ "Lowest draw calls"
};
@@ -1260,6 +1293,19 @@ static void draw_settings_overlay(effect_runtime *runtime)
if (ImGui::Combo("Filter by depth buffer format", reinterpret_cast<int *>(&s_format_filtering), depth_format_items, static_cast<int>(std::size(depth_format_items))))
{
reshade::set_config_value(nullptr, "DEPTH", "FilterFormat", s_format_filtering);
force_reset = true;
}
+ const char *const reversed_items[] = {
+ "Any",
+ "Prefer reversed",
+ "Exclude reversed"
+ };
+ if (ImGui::Combo("Filter by reversed depth", reinterpret_cast<int *>(&s_reversed_filtering), reversed_items, static_cast<int>(std::size(reversed_items))))
+ {
+ reshade::set_config_value(nullptr, "DEPTH", "FilterReversed", s_reversed_filtering);
+ force_reset = true;
+ }
+
if (bool copy_before_clear_operations = s_preserve_depth_buffers != 0;
ImGui::Checkbox("Copy depth buffer before clear operations", ©_before_clear_operations))
--
2.47.0
Last edit: 4 days 7 hours ago by Berlint.
Please Log in or Create an account to join the conversation.