debug: F12 post-effect sliders and a one-shot post-effect comparison sweep
This commit is contained in:
parent
f2aaf4081e
commit
995f690029
2 changed files with 321 additions and 0 deletions
|
|
@ -22,6 +22,10 @@
|
|||
|
||||
#include <rex/cvar.h>
|
||||
|
||||
// Guest-thread shared scene state (the post-sweep's shadow-readiness gate
|
||||
// reads g_shadow_have; OnCaptureFrameEnd runs on the same thread).
|
||||
#include "skate3_native_scene_state.h"
|
||||
|
||||
#include "generated/skate3_init.h"
|
||||
#include "skate3_screenshot.h"
|
||||
|
||||
|
|
@ -59,6 +63,22 @@ REXCVAR_DEFINE_INT32(skate3_native_render_snapshot_stride, 1, "Skate 3",
|
|||
"e.g. 12 = ~12 recorded frames/sec at 144fps)")
|
||||
.range(1, 32)
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
// Defined in skate3_native_scene.cpp (the HDR post-effect cvars the sweep
|
||||
// below drives).
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_bloom);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_haze);
|
||||
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_hdr_debug);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_shafts);
|
||||
REXCVAR_DEFINE_BOOL(
|
||||
skate3_native_render_post_sweep, false, "Skate 3",
|
||||
"One-shot HDR post-effect comparison sweep: once gameplay is on screen, "
|
||||
"steps through baseline (bloom/shafts/haze off), each effect alone, "
|
||||
"everything on, and the hdr_debug views (shaft plane / haze term / sky "
|
||||
"gate), settling between steps and writing one tagged screenshot per "
|
||||
"step (shot_<ts>_sweep_<step>.png), then restores the previous cvar "
|
||||
"values and turns itself off. Arm via config for a hands-off capture "
|
||||
"run, or from the debug dialog mid-session.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
REXCVAR_DEFINE_STRING(skate3_native_render_snapshot_dir, "native_render_snapshots", "Skate 3",
|
||||
"Directory for native-render guest memory snapshots and metadata")
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
|
|
@ -769,6 +789,135 @@ void OnCaptureFrameEnd(uint8_t* base, uint64_t frame_index,
|
|||
}
|
||||
f11_was_down = f11_down;
|
||||
}
|
||||
// Post-effect comparison sweep (skate3_native_render_post_sweep): one
|
||||
// session, one screenshot per render state: baseline, each HDR post
|
||||
// effect alone, everything on, then the hdr_debug diagnostic views.
|
||||
// Gameplay detection reuses the auto-snapshot heuristic (a sustained
|
||||
// full-scene mesh count); each step settles before its capture so
|
||||
// hot-reload toggles and exposure have taken effect.
|
||||
{
|
||||
struct SweepStep {
|
||||
const char* tag;
|
||||
bool bloom;
|
||||
bool shafts;
|
||||
bool haze;
|
||||
int32_t debug;
|
||||
};
|
||||
static constexpr SweepStep kSweepSteps[] = {
|
||||
{"base", false, false, false, 0},
|
||||
{"bloom", true, false, false, 0},
|
||||
{"shafts", false, true, false, 0},
|
||||
{"haze", false, false, true, 0},
|
||||
{"all", true, true, true, 0},
|
||||
{"dbg_shaftplane", true, true, true, 4},
|
||||
{"dbg_hazeterm", true, true, true, 5},
|
||||
};
|
||||
enum class SweepState { kIdle, kWaitGameplay, kStep };
|
||||
static SweepState sweep_state = SweepState::kIdle;
|
||||
static uint32_t sweep_gameplay_frames = 0;
|
||||
static size_t sweep_index = 0;
|
||||
static uint64_t sweep_capture_frame = 0;
|
||||
static bool sweep_captured = false;
|
||||
static bool saved_bloom = false, saved_shafts = false, saved_haze = false;
|
||||
static int32_t saved_debug = 0;
|
||||
static char sweep_ts[24] = {};
|
||||
// Every swept cvar is a per-frame hot read (no pipeline rebuilds), so
|
||||
// settling is short. Kept to a fraction of a second per step: quick
|
||||
// enough that the captures stay frame-comparable for offline A/B
|
||||
// diffing, slow enough to follow by eye.
|
||||
constexpr uint64_t kSweepSettleFrames = 48;
|
||||
const bool sweep_armed = REXCVAR_GET(skate3_native_render_post_sweep);
|
||||
const auto sweep_restore = [&] {
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom, saved_bloom);
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts, saved_shafts);
|
||||
REXCVAR_SET(skate3_native_render_scene_haze, saved_haze);
|
||||
REXCVAR_SET(skate3_native_render_scene_hdr_debug, saved_debug);
|
||||
REXCVAR_SET(skate3_native_render_post_sweep, false);
|
||||
sweep_state = SweepState::kIdle;
|
||||
};
|
||||
switch (sweep_state) {
|
||||
case SweepState::kIdle:
|
||||
if (sweep_armed) {
|
||||
sweep_state = SweepState::kWaitGameplay;
|
||||
sweep_gameplay_frames = 0;
|
||||
REXLOG_INFO("native-render post sweep: armed, waiting for gameplay");
|
||||
}
|
||||
break;
|
||||
case SweepState::kWaitGameplay:
|
||||
if (!sweep_armed) {
|
||||
sweep_state = SweepState::kIdle;
|
||||
break;
|
||||
}
|
||||
// Gameplay AND the frame-global shadow rows captured: the sweep's
|
||||
// shaft states are meaningless before the shadow pass is live, and
|
||||
// right after takeover the sweep otherwise outruns its bring-up.
|
||||
sweep_gameplay_frames =
|
||||
(mesh_count >= 300 && g_shadow_have) ? sweep_gameplay_frames + 1
|
||||
: 0;
|
||||
if (sweep_gameplay_frames >= 240) {
|
||||
saved_bloom = REXCVAR_GET(skate3_native_render_scene_bloom);
|
||||
saved_shafts = REXCVAR_GET(skate3_native_render_scene_shafts);
|
||||
saved_haze = REXCVAR_GET(skate3_native_render_scene_haze);
|
||||
saved_debug = REXCVAR_GET(skate3_native_render_scene_hdr_debug);
|
||||
const std::time_t t = std::time(nullptr);
|
||||
std::tm tm{};
|
||||
localtime_s(&tm, &t);
|
||||
std::snprintf(sweep_ts, sizeof(sweep_ts), "%02d%02d%02d", tm.tm_hour,
|
||||
tm.tm_min, tm.tm_sec);
|
||||
sweep_index = 0;
|
||||
sweep_captured = false;
|
||||
sweep_capture_frame = frame_index + kSweepSettleFrames;
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom, kSweepSteps[0].bloom);
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts, kSweepSteps[0].shafts);
|
||||
REXCVAR_SET(skate3_native_render_scene_haze, kSweepSteps[0].haze);
|
||||
REXCVAR_SET(skate3_native_render_scene_hdr_debug, kSweepSteps[0].debug);
|
||||
sweep_state = SweepState::kStep;
|
||||
REXLOG_INFO("native-render post sweep: started, tag {} ({} steps)",
|
||||
sweep_ts, std::size(kSweepSteps));
|
||||
}
|
||||
break;
|
||||
case SweepState::kStep:
|
||||
if (!sweep_armed) {
|
||||
sweep_restore();
|
||||
REXLOG_INFO("native-render post sweep: cancelled, cvars restored");
|
||||
break;
|
||||
}
|
||||
if (!sweep_captured && frame_index >= sweep_capture_frame) {
|
||||
char tag[48];
|
||||
std::snprintf(tag, sizeof(tag), "%s_sweep_%s", sweep_ts,
|
||||
kSweepSteps[sweep_index].tag);
|
||||
skate3::screenshot::CaptureWindow(
|
||||
skate3::screenshot::RememberedWindow(), tag);
|
||||
REXLOG_INFO("native-render post sweep: captured {} ({}/{})", tag,
|
||||
sweep_index + 1, std::size(kSweepSteps));
|
||||
sweep_captured = true;
|
||||
// A short hold so the (asynchronous) window grab reads this
|
||||
// state's frame before the next step's cvars apply.
|
||||
sweep_capture_frame = frame_index + 12;
|
||||
break;
|
||||
}
|
||||
if (sweep_captured && frame_index >= sweep_capture_frame) {
|
||||
if (++sweep_index >= std::size(kSweepSteps)) {
|
||||
sweep_restore();
|
||||
REXLOG_INFO(
|
||||
"native-render post sweep: done, cvars restored (tag {})",
|
||||
sweep_ts);
|
||||
break;
|
||||
}
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom,
|
||||
kSweepSteps[sweep_index].bloom);
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts,
|
||||
kSweepSteps[sweep_index].shafts);
|
||||
REXCVAR_SET(skate3_native_render_scene_haze,
|
||||
kSweepSteps[sweep_index].haze);
|
||||
REXCVAR_SET(skate3_native_render_scene_hdr_debug,
|
||||
kSweepSteps[sweep_index].debug);
|
||||
sweep_captured = false;
|
||||
sweep_capture_frame = frame_index + kSweepSettleFrames;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!g_collecting && frame_index % 32 == 0) {
|
||||
const std::filesystem::path trigger = SnapshotDir() / "trigger";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#include "skate3_native_debug_dialog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
|
|
@ -38,6 +41,27 @@ REXCVAR_DECLARE(bool, skate3_native_render_scene_tex_revalidate);
|
|||
REXCVAR_DECLARE(bool, skate3_native_render_scene_mesh_revalidate);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_tex_mips);
|
||||
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_debug);
|
||||
// HDR post-effect stack (hot; skate3_native_scene.cpp).
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_hdr);
|
||||
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_hdr_debug);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_bloom);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_bloom_threshold);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_bloom_knee);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_bloom_intensity);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_shafts);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_intensity);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_shafts_reach);
|
||||
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_shafts_steps);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_haze);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_haze_intensity);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_haze_density);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_ssao);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_ssao_radius);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_ssao_intensity);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_ssao_luma_protect);
|
||||
REXCVAR_DECLARE(bool, skate3_native_render_scene_sun_override);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_sun_azimuth);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_sun_elevation);
|
||||
REXCVAR_DECLARE(int32_t, skate3_native_render_scene_refl_mode);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_refl_lod);
|
||||
REXCVAR_DECLARE(double, skate3_native_render_scene_refl_bias_x);
|
||||
|
|
@ -66,6 +90,20 @@ bool CvarCheckbox(const char* label, bool value, const char* help = nullptr) {
|
|||
return v;
|
||||
}
|
||||
|
||||
// Float-cvar slider (the cvars are hot-reload doubles; edits apply next
|
||||
// frame). Returns the possibly-changed value for REXCVAR_SET.
|
||||
double CvarSlider(const char* label, double value, float lo, float hi,
|
||||
const char* fmt, const char* help = nullptr) {
|
||||
float v = float(value);
|
||||
if (ImGui::SliderFloat(label, &v, lo, hi, fmt)) {
|
||||
value = double(v);
|
||||
}
|
||||
if (help != nullptr && (ImGui::IsItemHovered() || ImGui::IsItemActive())) {
|
||||
ImGui::SetTooltip("%s", help);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void NativeDebugDialog::Show() {
|
||||
|
|
@ -158,6 +196,140 @@ void NativeDebugDialog::OnDraw(ImGuiIO& io) {
|
|||
"World env materials cull FRONT like the game's material "
|
||||
"XMLs; off = legacy cull-none (shows interior faces)"));
|
||||
|
||||
ImGui::SeparatorText("HDR post effects (live)");
|
||||
REXCVAR_SET(skate3_native_render_scene_hdr,
|
||||
CvarCheckbox("HDR intermediate",
|
||||
REXCVAR_GET(skate3_native_render_scene_hdr),
|
||||
"Float scene target + single host tonemap, the basis for "
|
||||
"bloom, shafts and haze. Off = the classic in-material "
|
||||
"tonemap (parity A/B)."));
|
||||
{
|
||||
int dbg = REXCVAR_GET(skate3_native_render_scene_hdr_debug);
|
||||
const char* kHdrDbg[] = {"0: off", "1: bloom term",
|
||||
"2: raw pre-tonemap", "3: AO plane",
|
||||
"4: shaft plane", "5: haze term"};
|
||||
if (ImGui::Combo("HDR debug view", &dbg, kHdrDbg, 6)) {
|
||||
REXCVAR_SET(skate3_native_render_scene_hdr_debug, dbg);
|
||||
}
|
||||
}
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts,
|
||||
CvarCheckbox("Volumetric sun shafts",
|
||||
REXCVAR_GET(skate3_native_render_scene_shafts),
|
||||
"Shadow-marched air: shadowed portions of each view ray "
|
||||
"proportionally dim the light seen through them (dark "
|
||||
"crepuscular shafts from buildings/trees/underpasses). "
|
||||
"Fully lit air is untouched."));
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts_intensity,
|
||||
CvarSlider("shaft intensity",
|
||||
REXCVAR_GET(skate3_native_render_scene_shafts_intensity),
|
||||
0.0f, 1.5f, "%.2f",
|
||||
"How strongly fully shadowed air dims the scene behind it "
|
||||
"(scaled by the forward-scatter phase toward the sun)."));
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts_reach,
|
||||
CvarSlider("shaft reach (world units)",
|
||||
REXCVAR_GET(skate3_native_render_scene_shafts_reach),
|
||||
5.0f, 300.0f, "%.0f",
|
||||
"How far in front of the camera the air is sampled. Longer "
|
||||
"= more distant shadow volumes, coarser sampling."));
|
||||
{
|
||||
int steps = REXCVAR_GET(skate3_native_render_scene_shafts_steps);
|
||||
if (ImGui::SliderInt("shaft steps", &steps, 8, 64)) {
|
||||
REXCVAR_SET(skate3_native_render_scene_shafts_steps, steps);
|
||||
}
|
||||
}
|
||||
REXCVAR_SET(skate3_native_render_scene_haze,
|
||||
CvarCheckbox("Directional haze",
|
||||
REXCVAR_GET(skate3_native_render_scene_haze),
|
||||
"Fog-tinted sun scattering added with view distance, "
|
||||
"strongest toward the sun. Off by default: the game's "
|
||||
"authored sky/fog already carry the base atmosphere."));
|
||||
REXCVAR_SET(skate3_native_render_scene_haze_intensity,
|
||||
CvarSlider("haze intensity",
|
||||
REXCVAR_GET(skate3_native_render_scene_haze_intensity),
|
||||
0.0f, 0.5f, "%.3f"));
|
||||
REXCVAR_SET(skate3_native_render_scene_haze_density,
|
||||
CvarSlider("haze density (1/unit)",
|
||||
REXCVAR_GET(skate3_native_render_scene_haze_density),
|
||||
0.0f, 0.02f, "%.4f",
|
||||
"How quickly the scattering saturates with distance "
|
||||
"(0.005 reaches ~63% at 200 units)."));
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom,
|
||||
CvarCheckbox("Bloom",
|
||||
REXCVAR_GET(skate3_native_render_scene_bloom),
|
||||
"Downsample/upsample pyramid driven by pre-tonemap "
|
||||
"brightness (night lamps, neon, sun glare)."));
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom_threshold,
|
||||
CvarSlider("bloom threshold",
|
||||
REXCVAR_GET(skate3_native_render_scene_bloom_threshold),
|
||||
0.0f, 1.5f, "%.2f",
|
||||
"Pre-tonemap onset (1.0 = the tone curve's saturation "
|
||||
"point). Below ~0.7 the sunlit day frame starts feeding "
|
||||
"the pyramid and veils in glow."));
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom_knee,
|
||||
CvarSlider("bloom knee",
|
||||
REXCVAR_GET(skate3_native_render_scene_bloom_knee),
|
||||
0.0f, 0.5f, "%.2f"));
|
||||
REXCVAR_SET(skate3_native_render_scene_bloom_intensity,
|
||||
CvarSlider("bloom intensity",
|
||||
REXCVAR_GET(skate3_native_render_scene_bloom_intensity),
|
||||
0.0f, 0.3f, "%.3f"));
|
||||
REXCVAR_SET(skate3_native_render_scene_ssao,
|
||||
CvarCheckbox("SSAO (GTAO)",
|
||||
REXCVAR_GET(skate3_native_render_scene_ssao),
|
||||
"Screen-space ambient occlusion: contact shading under "
|
||||
"ledges, rails, vehicles, feet."));
|
||||
REXCVAR_SET(skate3_native_render_scene_ssao_radius,
|
||||
CvarSlider("SSAO radius (world units)",
|
||||
REXCVAR_GET(skate3_native_render_scene_ssao_radius),
|
||||
0.1f, 4.0f, "%.2f"));
|
||||
REXCVAR_SET(skate3_native_render_scene_ssao_intensity,
|
||||
CvarSlider("SSAO intensity",
|
||||
REXCVAR_GET(skate3_native_render_scene_ssao_intensity),
|
||||
0.0f, 3.0f, "%.2f",
|
||||
"Exponent on the visibility term (1 = physical, >1 = "
|
||||
"accentuated)."));
|
||||
REXCVAR_SET(skate3_native_render_scene_ssao_luma_protect,
|
||||
CvarSlider("SSAO sunlit protection",
|
||||
REXCVAR_GET(skate3_native_render_scene_ssao_luma_protect),
|
||||
0.0f, 3.0f, "%.2f",
|
||||
"How strongly bright (sun-lit) surfaces resist SSAO "
|
||||
"darkening (ambient-only approximation)."));
|
||||
{
|
||||
const bool was_on = REXCVAR_GET(skate3_native_render_scene_sun_override);
|
||||
const bool now_on =
|
||||
CvarCheckbox("Sun override (lighting lab)", was_on,
|
||||
"Move the sun with the sliders below: dynamic CSM "
|
||||
"shadows, the static world-shadow map, shadow receivers "
|
||||
"and the volumetric shafts all follow. Baked lightmap "
|
||||
"shade and the sky dome's painted sun stay put (game "
|
||||
"content).");
|
||||
if (now_on && !was_on) {
|
||||
// Seed the sliders from the captured sun so enabling the override
|
||||
// starts at the true position instead of jumping.
|
||||
float sun[3];
|
||||
skate3::native_scene::GetCapturedSunDir(sun);
|
||||
const float kRad = 57.29577951f;
|
||||
REXCVAR_SET(skate3_native_render_scene_sun_azimuth,
|
||||
double(std::fmod(std::atan2(sun[0], sun[2]) * kRad + 360.0f,
|
||||
360.0f)));
|
||||
REXCVAR_SET(
|
||||
skate3_native_render_scene_sun_elevation,
|
||||
double(std::clamp(std::asin(std::clamp(sun[1], -1.0f, 1.0f)) * kRad,
|
||||
2.0f, 88.0f)));
|
||||
}
|
||||
REXCVAR_SET(skate3_native_render_scene_sun_override, now_on);
|
||||
}
|
||||
REXCVAR_SET(skate3_native_render_scene_sun_azimuth,
|
||||
CvarSlider("sun azimuth (deg)",
|
||||
REXCVAR_GET(skate3_native_render_scene_sun_azimuth),
|
||||
0.0f, 360.0f, "%.0f"));
|
||||
REXCVAR_SET(skate3_native_render_scene_sun_elevation,
|
||||
CvarSlider("sun elevation (deg)",
|
||||
REXCVAR_GET(skate3_native_render_scene_sun_elevation),
|
||||
2.0f, 88.0f, "%.0f",
|
||||
"Low elevations give long shadows and the most visible "
|
||||
"volumetric shafts."));
|
||||
|
||||
ImGui::SeparatorText("Reflective glass isolation (env fam 5/6/13)");
|
||||
{
|
||||
int mode = REXCVAR_GET(skate3_native_render_scene_refl_mode);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue