From 1cd0113ff228b0d765f437fb78213e779f7a4b22 Mon Sep 17 00:00:00 2001 From: mchughalex <25671999+mchughalex@users.noreply.github.com> Date: Mon, 20 Jul 2026 00:36:07 +0100 Subject: [PATCH] graphics: wide guest output for native ultrawide, drop the emulated Hor+ classifier --- include/rex/graphics/native_guest_renderer.h | 19 + include/rex/graphics/pipeline/texture/cache.h | 4 - include/rex/graphics/ultrawide_debug.h | 183 -- include/rex/kernel/guest_presence.h | 19 + .../ui/overlay/ultrawide_targets_overlay.h | 36 - src/core/CMakeLists.txt | 2 +- src/core/guest_presence.cpp | 24 + src/core/perf/counter.cpp | 13 +- src/core/skate3_ultrawide_classifier.inc | 184 -- src/core/ultrawide_debug.cpp | 1562 ----------------- src/graphics/command_processor.cpp | 12 - src/graphics/d3d12/command_processor.cpp | 286 +-- src/graphics/native_guest_renderer.cpp | 36 + src/graphics/pipeline/texture/cache.cpp | 148 -- src/graphics/vulkan/command_processor.cpp | 276 +-- src/kernel/xam/apps/xgi_app.cpp | 5 +- src/kernel/xboxkrnl/xboxkrnl_video.cpp | 20 +- src/ui/CMakeLists.txt | 1 - src/ui/overlay/simple_settings_overlay.cpp | 4 +- src/ui/overlay/ultrawide_targets_overlay.cpp | 1431 --------------- 20 files changed, 141 insertions(+), 4124 deletions(-) delete mode 100644 include/rex/graphics/ultrawide_debug.h create mode 100644 include/rex/kernel/guest_presence.h delete mode 100644 include/rex/ui/overlay/ultrawide_targets_overlay.h create mode 100644 src/core/guest_presence.cpp delete mode 100644 src/core/skate3_ultrawide_classifier.inc delete mode 100644 src/core/ultrawide_debug.cpp delete mode 100644 src/ui/overlay/ultrawide_targets_overlay.cpp diff --git a/include/rex/graphics/native_guest_renderer.h b/include/rex/graphics/native_guest_renderer.h index 8625548..a2ce48c 100644 --- a/include/rex/graphics/native_guest_renderer.h +++ b/include/rex/graphics/native_guest_renderer.h @@ -49,6 +49,25 @@ bool HasNativeGuestOutputRenderer(); // True while the registered renderer actually replaced the last presented // frame (false when it yields to the emulated output). bool IsNativeGuestOutputActive(); + +// ---- Wide guest output ---------------------------------------------------- +// Display aspect the native renderer wants to draw at when it is wider than +// the guest frontbuffer's own aspect (ultrawide). 0 = disabled. Set once at +// app startup; consumed by the command processors when sizing the guest +// output for a swap. +void SetNativeGuestOutputWideAspect(double aspect); +double GetNativeGuestOutputWideAspect(); +// Applies the wide aspect to this swap's guest output and display +// dimensions: widens guest_output_width to guest_output_height * aspect and +// makes the display aspect match. Only widens while a renderer is registered +// AND it served the last presented frame, so emulated fallback frames size +// back to the frontbuffer aspect and present pillarboxed by the presenter's +// letterbox path. Returns whether the output was widened; when it was and +// the renderer then yields the frame, the refresh callback must return false +// (keeping the previously presented image) instead of running the emulated +// blit, which writes frontbuffer-aspect content. +bool ApplyNativeGuestOutputWideAspect(uint32_t& guest_output_width, uint32_t guest_output_height, + uint32_t& display_width, uint32_t& display_height); // native_render_suppress_emulated_draws && IsNativeGuestOutputActive(): // command processors skip emulated draw/resolve execution (memexport draws, // fences, queries and PM4 parsing still run). diff --git a/include/rex/graphics/pipeline/texture/cache.h b/include/rex/graphics/pipeline/texture/cache.h index dec0767..33b2cca 100644 --- a/include/rex/graphics/pipeline/texture/cache.h +++ b/include/rex/graphics/pipeline/texture/cache.h @@ -612,10 +612,6 @@ class TextureCache { static bool DebugIsTeamProfileBackgroundCandidateKey(const TextureKey& key); void DebugLogTeamProfileBackgroundTextureCandidate(const char* action, uint32_t fetch_index, const Texture& texture); - bool DebugConsumeSkate3UltrawideTextureTraceLog(); - void DebugLogSkate3UltrawideTextureTrace(const char* action, uint32_t fetch_index, - const TextureKey& key, - const Texture* texture = nullptr); void UpdateTexturesTotalHostMemoryUsage(uint64_t add, uint64_t subtract); diff --git a/include/rex/graphics/ultrawide_debug.h b/include/rex/graphics/ultrawide_debug.h deleted file mode 100644 index ee5c9c4..0000000 --- a/include/rex/graphics/ultrawide_debug.h +++ /dev/null @@ -1,183 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace rex::graphics::ultrawide_debug { - -struct TargetKey { - std::array color_info{}; - uint32_t depth_info = 0; - uint32_t surface_info = 0; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - uint32_t color_mask = 0; - uint32_t depth_control = 0; - uint32_t pa_cl_vte_cntl = 0; - uint32_t primitive_type = 0; - uint32_t host_vertex_shader_type = 0; - uint64_t vertex_shader_hash = 0; - uint64_t pixel_shader_hash = 0; - - bool operator==(const TargetKey& other) const; -}; - -struct TargetEntry { - uint64_t hash = 0; - TargetKey key; - bool enabled = false; - bool default_enabled = false; - bool shadow_candidate = false; - uint64_t draw_count = 0; - uint64_t applied_draw_count = 0; - uint64_t last_seen_order = 0; -}; - -enum class ScreenCallbackKind : uint32_t { - kWidth = 0, - kHeight = 1, - kScreenFlag = 2, - kWide = 3, -}; - -struct ScreenCallbackEntry { - uint64_t hash = 0; - ScreenCallbackKind kind = ScreenCallbackKind::kWidth; - uint32_t caller_lr = 0; - uint32_t last_value = 0; - uint32_t last_returned_value = 0; - bool last_spoofed = false; - bool enabled = false; - uint64_t call_count = 0; - uint64_t last_seen_order = 0; -}; - -struct GuestStoreWatchEntry { - uint64_t sequence = 0; - std::string target; - uint32_t address = 0; - uint32_t value = 0; - uint32_t caller_lr = 0; - std::string function; - std::string stack; - uint64_t count = 1; -}; - -class GuestFunctionScope { - public: - explicit GuestFunctionScope(const char* function_name); - ~GuestFunctionScope(); - - private: - bool active_ = false; -}; - -enum class DrawBucket : uint32_t { - kMainColorDepth = 0, - kDepthOnly = 1, - kCopyResolve = 2, - kMemexport = 3, - kNoPixelShader = 4, -}; - -struct DrawFingerprint { - DrawBucket bucket = DrawBucket::kMainColorDepth; - uint32_t guest_render_object = 0; - uint32_t guest_render_vtable = 0; - uint32_t guest_render_target = 0; - uint64_t vertex_shader_hash = 0; - uint64_t pixel_shader_hash = 0; - uint32_t primitive_type = 0; - uint32_t vertex_count = 0; - uint32_t primitive_count = 0; - uint32_t packet_ptr = 0; - uint32_t index_guest_base = 0; - uint32_t index_length = 0; - static constexpr size_t kVertexFetchCount = 4; - uint32_t vertex_fetch_address[kVertexFetchCount] = {}; - uint32_t vertex_fetch_size[kVertexFetchCount] = {}; - static constexpr size_t kTextureFetchCount = 8; - uint64_t texture_key_hash[kTextureFetchCount] = {}; - uint32_t texture_fetch_index[kTextureFetchCount] = {}; - uint32_t texture_base_address[kTextureFetchCount] = {}; - uint32_t texture_base_length[kTextureFetchCount] = {}; - uint32_t texture_width[kTextureFetchCount] = {}; - uint32_t texture_height[kTextureFetchCount] = {}; - uint32_t texture_format[kTextureFetchCount] = {}; -}; - -struct DrawFingerprintEntry { - DrawFingerprint fingerprint; - uint64_t hash = 0; - bool enabled = true; - uint64_t draw_count = 0; - uint64_t submitted_count = 0; - uint64_t skipped_count = 0; - uint64_t vertices = 0; - uint64_t primitives = 0; - uint64_t last_seen_order = 0; - uint64_t last_seen_ms = 0; -}; - -enum class ApplyMode : uint32_t { - kTargetList = 0, - kForceAll = 1, - kForceNone = 2, - kSavedOnly = 3, -}; - -uint64_t HashKey(const TargetKey& key); -void NotifySkate3GameplayContext(uint32_t user_index, uint32_t context_id, uint32_t value); -bool IsSkate3GameplayUltrawideActive(); -// Live value of the Skate 3 presence context 0x8001 (1 = in gameplay, -// 0 = frontend / pause menu / loading). -uint32_t Skate3GameplayContextValue(); -bool ShouldApplyAndRecord(const TargetKey& key, bool default_enabled); -uint32_t ShadowMapCandidateLevel(const TargetKey& key); -bool IsShadowMapCandidate(const TargetKey& key, uint32_t mode = 1); -std::vector SnapshotTargets(); -void SetTargetEnabled(uint64_t hash, bool enabled); -void SetAllTargetsEnabled(bool enabled); -void ResetTargetOverrides(); -void SetTargetRecordingActive(bool active); -uint64_t HashScreenCallback(ScreenCallbackKind kind, uint32_t caller_lr); -bool RecordScreenCallback(ScreenCallbackKind kind, uint32_t caller_lr, uint32_t value); -void RecordScreenCallbackResult(ScreenCallbackKind kind, uint32_t caller_lr, uint32_t native_value, - uint32_t returned_value); -std::vector SnapshotScreenCallbacks(); -void SetScreenCallbackEnabled(uint64_t hash, bool enabled); -void SetAllScreenCallbacksEnabled(bool enabled); -void SetGuestStoreWatchTargets(uint32_t index_base, uint32_t vertex_fetch_address, - uint32_t texture_base_address, uint64_t group_hash = 0); -void ClearGuestStoreWatchTargets(); -void ClearGuestStoreWatchEvents(); -void SetGuestStoreWatchLogPath(const std::filesystem::path& path); -std::vector SnapshotGuestStoreWatchEvents(); -void RecordGuestStoreU32(uint32_t address, uint32_t value, const char* function_name, - uint32_t caller_lr = 0); -void RecordDrawWatchTransition(bool present, uint64_t age, uint32_t packet_ptr, - uint32_t index_base, uint32_t vertex_fetch_address, - uint64_t group_hash); -bool IsGuestStoreWatchActive(); -void SetCurrentDrawOwner(uint32_t object, uint32_t vtable, uint32_t target); -void ClearCurrentDrawOwner(); -uint64_t HashDrawFingerprint(const DrawFingerprint& fingerprint); -bool RecordDrawFingerprint(const DrawFingerprint& fingerprint); -std::vector SnapshotDrawFingerprints(); -void SetDrawFingerprintEnabled(uint64_t hash, bool enabled); -void SetAllDrawFingerprintsEnabled(bool enabled); -void ResetDrawFingerprintOverrides(); -void ClearDrawFingerprints(); -ApplyMode GetApplyMode(); -void SetApplyMode(ApplyMode mode); -void LoadBuiltInSkate3Classifier(); -std::optional LoadTargets(const std::filesystem::path& path); -std::optional SaveTargets(const std::filesystem::path& path); - -} // namespace rex::graphics::ultrawide_debug diff --git a/include/rex/kernel/guest_presence.h b/include/rex/kernel/guest_presence.h new file mode 100644 index 0000000..ebe104c --- /dev/null +++ b/include/rex/kernel/guest_presence.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +// Live guest presence context, published by XGI XUserSetContext (xam). Lives +// in the shared core library so every module (kernel, graphics backends, the +// host app) reads the same state. + +namespace rex::kernel::guest_presence { + +// Called from the XGI XUserSetContext handler with every context write. +// Only user 0's presence context 0x8001 is tracked; other writes are ignored. +void NotifyContext(uint32_t user_index, uint32_t context_id, uint32_t value); + +// Live value of the Skate 3 presence context 0x8001 (1 = in gameplay, +// 0 = frontend / pause menu / loading). +uint32_t GameplayContextValue(); + +} // namespace rex::kernel::guest_presence diff --git a/include/rex/ui/overlay/ultrawide_targets_overlay.h b/include/rex/ui/overlay/ultrawide_targets_overlay.h deleted file mode 100644 index c412af1..0000000 --- a/include/rex/ui/overlay/ultrawide_targets_overlay.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace rex::ui { - -class UltrawideTargetsDialog final : public ImGuiDialog { - public: - using CloseCallback = std::function; - - UltrawideTargetsDialog(ImGuiDrawer* drawer, std::filesystem::path export_path, - CloseCallback close_callback = {}); - ~UltrawideTargetsDialog(); - - void Show(); - void Toggle(); - void Hide(); - bool visible() const { return visible_; } - - protected: - void OnDraw(ImGuiIO& io) override; - - private: - std::filesystem::path export_path_; - CloseCallback close_callback_; - bool visible_ = false; - bool show_only_seen_ = true; - bool draw_section_expanded_ = false; - bool draw_show_exact_fingerprints_ = false; - int draw_group_mode_ = 1; -}; - -} // namespace rex::ui diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a365aa..1f8f278 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(rexcore OBJECT exception_handler.cpp filesystem.cpp filesystem_wildcard.cpp + guest_presence.cpp logging.cpp memory.cpp mutex.cpp @@ -20,7 +21,6 @@ add_library(rexcore OBJECT string_buffer.cpp threading.cpp timer_queue.cpp - ultrawide_debug.cpp utf8.cpp vec128.cpp ) diff --git a/src/core/guest_presence.cpp b/src/core/guest_presence.cpp new file mode 100644 index 0000000..644a6e5 --- /dev/null +++ b/src/core/guest_presence.cpp @@ -0,0 +1,24 @@ +#include + +#include + +namespace rex::kernel::guest_presence { +namespace { + +std::atomic g_gameplay_context_value{0}; + +} // namespace + +void NotifyContext(uint32_t user_index, uint32_t context_id, uint32_t value) { + constexpr uint32_t kGameplayContext = 0x8001; + if (user_index != 0 || context_id != kGameplayContext) { + return; + } + g_gameplay_context_value.store(value, std::memory_order_relaxed); +} + +uint32_t GameplayContextValue() { + return g_gameplay_context_value.load(std::memory_order_relaxed); +} + +} // namespace rex::kernel::guest_presence diff --git a/src/core/perf/counter.cpp b/src/core/perf/counter.cpp index 5f471d9..4176420 100644 --- a/src/core/perf/counter.cpp +++ b/src/core/perf/counter.cpp @@ -688,16 +688,8 @@ bool ShouldCollectDrawDiagnostics() { return REXCVAR_GET(perf_diagnostics) || IsCaptureRecording(); } -// skate3_ultrawide_trace_draws lives in another DLL, so it is reachable only -// through the by-name registry query (mutex + map lookup), far too expensive -// for the per-draw call sites of ShouldCollectDrawFingerprints(). Cache the -// value and refresh it once per frame from ResetFrameCounters(). -static std::atomic g_trace_draws_cvar_cache{false}; - bool ShouldCollectDrawFingerprints() { - return REXCVAR_GET(perf_draw_fingerprints) || - g_trace_draws_cvar_cache.load(std::memory_order_relaxed) || - ShouldCollectDrawDiagnostics(); + return REXCVAR_GET(perf_draw_fingerprints) || ShouldCollectDrawDiagnostics(); } int64_t GetCounter(CounterId id) { @@ -705,9 +697,6 @@ int64_t GetCounter(CounterId id) { } void ResetFrameCounters() { - g_trace_draws_cvar_cache.store( - rex::cvar::Query("skate3_ultrawide_trace_draws"), - std::memory_order_relaxed); for (size_t i = 0; i < kNumCounters; ++i) { if (kIsGauge[i]) { // Gauges: snapshot the current value, don't zero diff --git a/src/core/skate3_ultrawide_classifier.inc b/src/core/skate3_ultrawide_classifier.inc deleted file mode 100644 index 26d2e6f..0000000 --- a/src/core/skate3_ultrawide_classifier.inc +++ /dev/null @@ -1,184 +0,0 @@ -// Generated from skate3_ultrawide_targets.toml. Do not edit by hand. -constexpr BuiltInTargetOverride kBuiltInSkate3TargetOverrides[] = { - {UINT64_C(0xA78541591A629661), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x09020258), UINT32_C(0x0000000F), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0xA4A965C189287B99)}, true}, - {UINT64_C(0x9D52F6177C7B389D), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0xA4A965C189287B99)}, true}, - {UINT64_C(0xBFD1461001CB97E4), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x0DFDF1BCC0E7B56D), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xC205003194EFBC71), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x28D0A573402806EB), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xD878108EB66E9B28), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x07CF21EDB84C9E22), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF9AB52AE558D3F50), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x00000000), UINT32_C(0x04020118), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x076AF30F69242406), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x00000000), UINT32_C(0x04020118), UINT32_C(0x00000000), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xA4EF713C0B27236D), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x00000000), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x8197FF5E7840DA63), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x9971A6822FCF2DD2), UINT64_C(0x12049EA773BAF206)}, true}, - {UINT64_C(0xCE27E0AA3C30B48A), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x01799B5C04C8EFE8), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xDD4117074EF2EAA6), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1F005A21696CC37E), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xFDC400257C26BEFC), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x38B43E35C163614C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x61EFEEA3DF226046), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1153D3552EF6E624), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x40E624D71F989930), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x00000000), UINT32_C(0x0C020320), UINT32_C(0x00000000), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xEF3B5EA129E40669), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xEB009E6404D91B5A), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x9971A6822FCF2DD2), UINT64_C(0x70E709E9650F8CC0)}, true}, - {UINT64_C(0x30625B4C1DF99FE8), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x9971A6822FCF2DD2), UINT64_C(0x8E01B3337D22E487)}, true}, - {UINT64_C(0xA9446AF4EF2988DA), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1F005A21696CC37E), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF82D5E2AA3448EC8), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x38B43E35C163614C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xCDB5E7DDBF3745FA), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1153D3552EF6E624), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x586B971BC5301FD6), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x9971A6822FCF2DD2), UINT64_C(0xDCFF2237FACA6FA2)}, true}, - {UINT64_C(0x5A53AE14E6682AF0), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x9971A6822FCF2DD2), UINT64_C(0x6D81B22A5A72F2F7)}, true}, - {UINT64_C(0x9B7DAC17F3087CA7), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x8A6134DE7D8980C0), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x56CF04EDC1C67AD2), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1F005A21696CC37E), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x630D4F6C8FF3BAC3), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC0A7FEA59D737772), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF3FB10C786C01BBF), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xAAE4AE2E54C97D0F), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x2B13F106B03A4C7D), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x69162B400E55DAC4), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xE47983C5D73AB140), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x38B43E35C163614C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x0B7CE4C6F4A49838), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x99340CC09E2F9371), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x8F9F6723F328A425), UINT64_C(0xAA0DF76CD5E0EDC6)}, true}, - {UINT64_C(0xE4F667D1EF0C3541), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xA43EDAB8CF26AC43), UINT64_C(0x1C8C6412B70D4904)}, true}, - {UINT64_C(0x4CC45A2F1E231172), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0xA43EDAB8CF26AC43), UINT64_C(0x1C8C6412B70D4904)}, true}, - {UINT64_C(0x6ACFCA21C84DD04F), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x0A020280), UINT32_C(0x00000000), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xEC8FBC04212C0A4B), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x0A020280), UINT32_C(0x00000000), UINT32_C(0x00000076), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xD541A82D0F859592), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x09020258), UINT32_C(0x00000000), UINT32_C(0x00000076), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xE5F9E8A8BD7B81FF), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x00000000), UINT32_C(0x00000076), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xE380D9E97118C6B7), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x156953004B00BE2F), UINT64_C(0x6122239794A98F31)}, true}, - {UINT64_C(0xAB60CA4C0329178E), {SemanticViewportClass::kUnknown, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000040), UINT32_C(0x00000020), UINT32_C(0x000002E0), UINT32_C(0x00800050), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0x3A7C78D0EB806EE8)}, true}, - {UINT64_C(0xD59F8C3273E11AB5), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000090), UINT32_C(0x00000050), UINT32_C(0x000002E0), UINT32_C(0x01800050), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0x3A7C78D0EB806EE8)}, true}, - {UINT64_C(0x2769C85DCDEB8FDC), {SemanticViewportClass::kUnknown, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000040), UINT32_C(0x00000020), UINT32_C(0x000002E0), UINT32_C(0x00800050), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0xE12EB29D7EA6D021)}, true}, - {UINT64_C(0x79A2F6E528FFF85B), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000090), UINT32_C(0x00000050), UINT32_C(0x000002E0), UINT32_C(0x01800050), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0xE12EB29D7EA6D021)}, true}, - {UINT64_C(0xE25A14DA07768B39), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000240), UINT32_C(0x00000140), UINT32_C(0x000002E0), UINT32_C(0x04800140), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x34FDABFF54D0767C), UINT64_C(0x670F9657E351B89B)}, true}, - {UINT64_C(0xD899F54B1EFCB5C4), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x1591D381BF0A7CE5), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x2A6DB4A54C1D6A44), UINT64_C(0x06CA19234014B7CF)}, true}, - {UINT64_C(0x199AA29FA7991FC5), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x2A6DB4A54C1D6A44), UINT64_C(0xEFAEF20D9DD3A856)}, true}, - {UINT64_C(0x85508A201CA4F08B), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x229B213C18BF3580), UINT64_C(0x002060C7FFA5DBEE)}, true}, - {UINT64_C(0x01A2EDFB77F55C72), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC186F78859736B49), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x4098E5BF99169505), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB814BA67EA32F032), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x9D3ACA8E7E627D61), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708733), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC186F78859736B49), UINT64_C(0xEB864605A91539ED)}, true}, - {UINT64_C(0x96D40D4C80EFF24A), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708733), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB814BA67EA32F032), UINT64_C(0xE1D8B5E58C05821E)}, true}, - {UINT64_C(0x1FC318709FD6C1D9), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x826E70D1F5709AB4)}, true}, - {UINT64_C(0x7154460DEA2784F2), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0xBD90261648A8EE85)}, true}, - {UINT64_C(0xD2593ADB4AABB99E), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x7386295CA842ED62)}, true}, - {UINT64_C(0xED4AE420F8A90FAF), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xB3F551C98DA3B0D8), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF2CC74A09A11CAB3), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x15C0DC717C5BE5C0), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x129E7F5770905F10), UINT64_C(0x437E40C3B326C513)}, true}, - {UINT64_C(0x50A5FDF3723B2EE6), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xBBD7F3BD8E43C865), UINT64_C(0x26A449F09A551230)}, true}, - {UINT64_C(0xDD5AABF82064D371), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0D70BBC32834DD35)}, true}, - {UINT64_C(0x34F99C310F21DAE1), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x826E70D1F5709AB4)}, true}, - {UINT64_C(0x623695D495D11C5A), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0xBD90261648A8EE85)}, true}, - {UINT64_C(0xEA1E4969126AB23C), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0BFC7C383593DB01)}, true}, - {UINT64_C(0x05CC09312C3E9F9B), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x300AB8E7F58DD49E)}, true}, - {UINT64_C(0x66DE18BBF0EC8645), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x903FD6B5FDBD396C)}, true}, - {UINT64_C(0x771C5EDB6865E300), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0xAA010FA0ACB4A2DE)}, true}, - {UINT64_C(0x0D8656F581B26A36), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x7386295CA842ED62)}, true}, - {UINT64_C(0x82D90DCCF0EF67D4), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x6D83BC72961EFDD6), UINT64_C(0x703CCA8334D5F0D3)}, true}, - {UINT64_C(0x77FFFC7414262CCC), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x80F5E44CF8F8C0F6), UINT64_C(0x6A7FC4375C8A174E)}, true}, - {UINT64_C(0x4DB8FFE5868CE91E), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB56724DBA4CA4FE0), UINT64_C(0xC24E796B8CA7A91A)}, true}, - {UINT64_C(0x6FA9EAA188A8E9A8), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x2645EEDA2D96AD00), UINT64_C(0x4E669779FCFA28A7)}, true}, - {UINT64_C(0xD62FCC7C5DA7DF5F), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x3E6FCA0DE0B3A8C2), UINT64_C(0xB3F33593C6D09C78)}, true}, - {UINT64_C(0xA2476D49450F3D33), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xA0BE8B27220B13DA), UINT64_C(0xAE440AA8E3BD1EFF)}, true}, - {UINT64_C(0x11A78BDBB7A54683), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x843B72BABFC575D2), UINT64_C(0x273A3914C45D195F)}, true}, - {UINT64_C(0x4878EFC57C23F637), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x77B1A22643B2D4A5), UINT64_C(0x2650312EFC7A723E)}, true}, - {UINT64_C(0x3CBC4808BED3C0CE), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xF98383D7822FCD4B), UINT64_C(0xB8659B1565935E42)}, true}, - {UINT64_C(0x157511950F5C4257), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x57FCA2530F71C768), UINT64_C(0x31E37C33EEAE5603)}, true}, - {UINT64_C(0x67D5ABC6E2419EBC), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xFF866FD9D067A561), UINT64_C(0x31E37C33EEAE5603)}, true}, - {UINT64_C(0x1CF9353AA1132855), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xBF42AC470AA4CE13), UINT64_C(0xFF772F11F9DA66C7)}, true}, - {UINT64_C(0xC59FCB667E0B50CA), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB35C2C3AC53D594A), UINT64_C(0x59F928CA74FC1F1B)}, true}, - {UINT64_C(0xD2B3995707485985), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x2BA7E44E7E1A1176), UINT64_C(0x4CDED5DED927F9B0)}, true}, - {UINT64_C(0x8B1DB6B090F019E4), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xF91B29D8FC044DBF), UINT64_C(0x959B4F51AB838BB0)}, true}, - {UINT64_C(0x3F5A4F2F89867D02), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x112A2355CC0D704D), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x1D77B495DAD1A0ED), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB4E21448711110DE), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x576ABA2EB83FD092), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x9176F3F9F09C4B6D), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xC4D3A56D246F5008), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x09020258), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x3E9C933709DBFC34), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x2A6DB4A54C1D6A44), UINT64_C(0x06CA19234014B7CF)}, true}, - {UINT64_C(0x1043548A365028B4), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x2A6DB4A54C1D6A44), UINT64_C(0xEFAEF20D9DD3A856)}, true}, - {UINT64_C(0xE44B22B012480B7E), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x229B213C18BF3580), UINT64_C(0x002060C7FFA5DBEE)}, true}, - {UINT64_C(0xDF2B9CC0644CF96B), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC186F78859736B49), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xE53ACE77F5E03E94), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB814BA67EA32F032), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x07F4727750423C34), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708733), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC186F78859736B49), UINT64_C(0xEB864605A91539ED)}, true}, - {UINT64_C(0xBAF6F0A8238A1617), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708733), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB814BA67EA32F032), UINT64_C(0xE1D8B5E58C05821E)}, true}, - {UINT64_C(0x95825DA3BEB63618), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x826E70D1F5709AB4)}, true}, - {UINT64_C(0xA86486AAEC6B8FD3), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0xBD90261648A8EE85)}, true}, - {UINT64_C(0x219888D0C81A4EBB), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x7386295CA842ED62)}, true}, - {UINT64_C(0xFE2CEB59D8971A5A), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x6DBBC8AC52778C65), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF8D6304318883E2E), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x6F421B04AD229861), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x129E7F5770905F10), UINT64_C(0x437E40C3B326C513)}, true}, - {UINT64_C(0xBE24F96A2F86A743), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xBBD7F3BD8E43C865), UINT64_C(0x26A449F09A551230)}, true}, - {UINT64_C(0xBC63805B609732D4), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0D70BBC32834DD35)}, true}, - {UINT64_C(0xF88AEB71252E4AC0), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4DA02D43D609305C), UINT64_C(0x826E70D1F5709AB4)}, true}, - {UINT64_C(0x045001575D46C1BB), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x87E91F0B7DE6C7A5), UINT64_C(0xBD90261648A8EE85)}, true}, - {UINT64_C(0xB48E7907D9CC5CDD), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0BFC7C383593DB01)}, true}, - {UINT64_C(0x6C53B91F9E7CDEEA), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x300AB8E7F58DD49E)}, true}, - {UINT64_C(0x1A2A2338B4866624), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x903FD6B5FDBD396C)}, true}, - {UINT64_C(0xAD058C3644007689), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0xAA010FA0ACB4A2DE)}, true}, - {UINT64_C(0xA376541F441E4FB3), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x4884B48A4C4D42BD), UINT64_C(0x7386295CA842ED62)}, true}, - {UINT64_C(0x5B5BDF4A7233BCB5), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x6D83BC72961EFDD6), UINT64_C(0x703CCA8334D5F0D3)}, true}, - {UINT64_C(0x1D397F204E732829), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x80F5E44CF8F8C0F6), UINT64_C(0x6A7FC4375C8A174E)}, true}, - {UINT64_C(0x5E9A899407A60B07), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB56724DBA4CA4FE0), UINT64_C(0xC24E796B8CA7A91A)}, true}, - {UINT64_C(0xBFF2A6E479D2494D), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x2645EEDA2D96AD00), UINT64_C(0x4E669779FCFA28A7)}, true}, - {UINT64_C(0x0562682FA93BDFC6), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x3E6FCA0DE0B3A8C2), UINT64_C(0xB3F33593C6D09C78)}, true}, - {UINT64_C(0x5933941C92C6ACE2), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xA0BE8B27220B13DA), UINT64_C(0xAE440AA8E3BD1EFF)}, true}, - {UINT64_C(0x5490C50558B0A6FA), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x843B72BABFC575D2), UINT64_C(0x273A3914C45D195F)}, true}, - {UINT64_C(0x8E3E16B4041D10A6), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x77B1A22643B2D4A5), UINT64_C(0x2650312EFC7A723E)}, true}, - {UINT64_C(0x80D634DEEEFA3023), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xF98383D7822FCD4B), UINT64_C(0xB8659B1565935E42)}, true}, - {UINT64_C(0x3B4A27CE871F46DE), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x57FCA2530F71C768), UINT64_C(0x31E37C33EEAE5603)}, true}, - {UINT64_C(0xAFD8D05CE702C08D), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xFF866FD9D067A561), UINT64_C(0x31E37C33EEAE5603)}, true}, - {UINT64_C(0xFB8DCC055DF6E894), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xBF42AC470AA4CE13), UINT64_C(0xFF772F11F9DA66C7)}, true}, - {UINT64_C(0x86158F7A9968A9FF), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB35C2C3AC53D594A), UINT64_C(0x59F928CA74FC1F1B)}, true}, - {UINT64_C(0x546215D3B88C4748), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x2BA7E44E7E1A1176), UINT64_C(0x4CDED5DED927F9B0)}, true}, - {UINT64_C(0xF6B8FF0D0BDCD5AD), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xF91B29D8FC044DBF), UINT64_C(0x959B4F51AB838BB0)}, true}, - {UINT64_C(0x4F1CF2D9F10DDC4F), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x112A2355CC0D704D), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x67D152C6CBD00FD8), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB4E21448711110DE), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x09BC09F4487FDEEF), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x9176F3F9F09C4B6D), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xEEAE693F419ED9C4), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0xA4A965C189287B99)}, true}, - {UINT64_C(0xC4EC310C78501C89), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0D70BBC32834DD35)}, true}, - {UINT64_C(0x57C306778FFCE634), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x7E8329B3B34B568C), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0D70BBC32834DD35)}, true}, - {UINT64_C(0x9AF67C1814B20861), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x11D7AC81789C78E2), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xBCF17602372C8923), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x768D0C1832BAA99F), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x863A82C331CA4E7A), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xDD8686FDE7D4FE2F), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xA1039E2BA9F6F0F2), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x1153D3552EF6E624), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xD30D492BB72A8E84), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0BFC7C383593DB01)}, true}, - {UINT64_C(0x6C53DC64C88E5653), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x300AB8E7F58DD49E)}, true}, - {UINT64_C(0x112DABFB9C0D2C5D), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x903FD6B5FDBD396C)}, true}, - {UINT64_C(0x75445340AA61B6B8), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0xAA010FA0ACB4A2DE)}, true}, - {UINT64_C(0xFBFF33846B090543), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x19B440C9ECEB7000), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x7A1B51CF16B6E6BA), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xF122FFFE0AC6ACA0), {SemanticViewportClass::kDoubleWideGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000280), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x46DAB5A8908F95E5), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0BFC7C383593DB01)}, true}, - {UINT64_C(0x3F49FEE14362C5E2), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x300AB8E7F58DD49E)}, true}, - {UINT64_C(0x69EF120F538756DC), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x903FD6B5FDBD396C)}, true}, - {UINT64_C(0xEADF80DF6BCFBDA1), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000007), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0xAA010FA0ACB4A2DE)}, true}, - {UINT64_C(0x130AEC24E5FEB916), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5678306911DBFA21), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x544BEB932F0B2CCD), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xB59486CC29609065), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x457ECAD3E8859627), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xEECAE2FDF2D987A8), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0xD465D0FF609B8C01), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x10807D45C83F929C), UINT64_C(0x0000000000000000)}, true}, - {UINT64_C(0x992F65716DDE5AB0), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120104B0), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0xA4A965C189287B99)}, true}, - {UINT64_C(0x47CEC4DBD2618777), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700732), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x3D5BF97DD0CF2BF4), UINT64_C(0xB1592798F0CD1839)}, true}, - {UINT64_C(0x16721B512CC8E52A), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700736), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0xE91029D930637F6E), UINT64_C(0x38818B3E2ED482F4)}, true}, - {UINT64_C(0xA2D3BF5F1FAE89A5), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x08000230), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x5AB7345B9857FD4B), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x6F2FE2DB5E2C9D9B), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x8A6134DE7D8980C0), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x3819C68257AAB4A7), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC0A7FEA59D737772), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x161CCBE6FB7FA2CB), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xAAE4AE2E54C97D0F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x0C945E0D516ACB09), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x69162B400E55DAC4), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x593760193C97EA86), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xDD8686FDE7D4FE2F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x1760A25BDB3C3A8F), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x768D0C1832BAA99F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0xF43F611A283B53B4), {SemanticViewportClass::kSquare, UINT32_C(0x00000800), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x3F7D7B06780F7147), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0xD760920DBAB9A3BF), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x8A6134DE7D8980C0), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x26BDC6C599B4969B), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xC0A7FEA59D737772), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x0B1AAB009E382577), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xAAE4AE2E54C97D0F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0xA965E653AFB7AA55), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x69162B400E55DAC4), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0xCF98B5A98C16103B), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0x768D0C1832BAA99F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x1287298BB43864D2), {SemanticViewportClass::kSquare, UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x00000400), UINT32_C(0x00000400), UINT32_C(0x00000000), UINT32_C(0x18000640), UINT32_C(0x00000000), UINT32_C(0x00708737), UINT32_C(0x0000043F), UINT32_C(0x00000004), UINT32_C(0x00000000), UINT64_C(0xDD8686FDE7D4FE2F), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0xB8ED22303014A9A0), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x47A7554B0F7A34EC), UINT64_C(0xC9BD70547A68F4C5)}, false}, - {UINT64_C(0xAE5EF820D5073D51), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x0000000F), UINT32_C(0x00700770), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x3EC739DF445745A9), UINT64_C(0x36F744866629EE18)}, false}, - {UINT64_C(0xE28F8BEB4E09C12D), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x0000000F), UINT32_C(0x00700770), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x3EC739DF445745A9), UINT64_C(0x7AF459B0BFF69478)}, false}, - {UINT64_C(0xBFC1B2D8F2E33B38), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000900), UINT32_C(0x00000500), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x8895AB90906ECBC1), UINT64_C(0x5928557DC4F52989)}, false}, - {UINT64_C(0x9560A636E29DFA8A), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000240), UINT32_C(0x00000140), UINT32_C(0x000002E0), UINT32_C(0x04800140), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0x8064167EECA65C8D)}, false}, - {UINT64_C(0x130889F224084C65), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000240), UINT32_C(0x00000140), UINT32_C(0x000002E0), UINT32_C(0x04800140), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0x35754B7A87E5683D)}, false}, - {UINT64_C(0xAD7D02EB7119AD40), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000240), UINT32_C(0x00000140), UINT32_C(0x000002E0), UINT32_C(0x04800140), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x00000006), UINT32_C(0x00000000), UINT64_C(0x08016D40A0EF5371), UINT64_C(0x3A7C78D0EB806EE8)}, false}, - {UINT64_C(0xCC1AC526FE146AB0), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x3D5BF97DD0CF2BF4), UINT64_C(0xB1592798F0CD1839)}, false}, - {UINT64_C(0x6329B6390DA4F1F9), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x0A020280), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x4001CB4958EAF13D), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x0A020280), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0xA4A965C189287B99)}, false}, - {UINT64_C(0xAF9DF4D3A87129B8), {SemanticViewportClass::kGameAspect, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000A00), UINT32_C(0x000005A0), UINT32_C(0x000002D0), UINT32_C(0x14000500), UINT32_C(0x0000000F), UINT32_C(0x00700730), UINT32_C(0x0000043F), UINT32_C(0x0000000D), UINT32_C(0x00000000), UINT64_C(0x166196AE4F110875), UINT64_C(0x39004B0872361843)}, false}, - {UINT64_C(0xBD81D7A062FC455D), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00000300), UINT32_C(0x00000001), UINT32_C(0x00000000), UINT64_C(0xB6C9863F710683EC), UINT64_C(0x0000000000000000)}, false}, - {UINT64_C(0x73EBDF3D39AFE8D4), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002E0), UINT32_C(0x120004B0), UINT32_C(0x0000000F), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0xA4A965C189287B99)}, false}, - {UINT64_C(0x8CC88D3928860FC0), {SemanticViewportClass::kSquare, UINT32_C(0x00000000), UINT32_C(0x00000000), UINT32_C(0x00004000), UINT32_C(0x00004000), UINT32_C(0x000002D0), UINT32_C(0x0A020280), UINT32_C(0x0000000F), UINT32_C(0x00008777), UINT32_C(0x00000300), UINT32_C(0x00000008), UINT32_C(0x00000000), UINT64_C(0x1E6883FCCDE1F688), UINT64_C(0xA4A965C189287B99)}, false}, -}; diff --git a/src/core/ultrawide_debug.cpp b/src/core/ultrawide_debug.cpp deleted file mode 100644 index be5532d..0000000 --- a/src/core/ultrawide_debug.cpp +++ /dev/null @@ -1,1562 +0,0 @@ -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -REXCVAR_DEFINE_BOOL(skate3_ultrawide_screen_callback_tracking, false, "Skate 3/Ultrawide", - "Track guest screen callback callers in the F7 ultrawide overlay") - .lifecycle(rex::cvar::Lifecycle::kHotReload); - -namespace rex::graphics::ultrawide_debug { - -namespace { - -void HashAdd(uint64_t& hash, uint64_t value); - -struct TargetKeyHash { - size_t operator()(const TargetKey& key) const { - return size_t(HashKey(key)); - } -}; - -enum class SemanticViewportClass : uint32_t { - kUnknown = 0, - kGameAspect = 1, - kDoubleWideGameAspect = 2, - kSquare = 3, -}; - -struct NormalizedViewport { - SemanticViewportClass viewport_class = SemanticViewportClass::kUnknown; - uint32_t x = 0; - uint32_t y = 0; - uint32_t width = 0; - uint32_t height = 0; -}; - -struct SemanticTargetKey { - SemanticViewportClass viewport_class = SemanticViewportClass::kUnknown; - uint32_t viewport_x = 0; - uint32_t viewport_y = 0; - uint32_t viewport_width = 0; - uint32_t viewport_height = 0; - uint32_t depth_info = 0; - uint32_t surface_info = 0; - uint32_t color_mask = 0; - uint32_t depth_control = 0; - uint32_t pa_cl_vte_cntl = 0; - uint32_t primitive_type = 0; - uint32_t host_vertex_shader_type = 0; - uint64_t vertex_shader_hash = 0; - uint64_t pixel_shader_hash = 0; - - bool operator==(const SemanticTargetKey& other) const { - return viewport_class == other.viewport_class && color_mask == other.color_mask && - viewport_x == other.viewport_x && viewport_y == other.viewport_y && - viewport_width == other.viewport_width && viewport_height == other.viewport_height && - depth_info == other.depth_info && surface_info == other.surface_info && - depth_control == other.depth_control && pa_cl_vte_cntl == other.pa_cl_vte_cntl && - primitive_type == other.primitive_type && - host_vertex_shader_type == other.host_vertex_shader_type && - vertex_shader_hash == other.vertex_shader_hash && - pixel_shader_hash == other.pixel_shader_hash; - } -}; - -struct SemanticTargetKeyHash { - size_t operator()(const SemanticTargetKey& key) const { - uint64_t hash = 1469598103934665603ull; - HashAdd(hash, uint32_t(key.viewport_class)); - HashAdd(hash, key.viewport_x); - HashAdd(hash, key.viewport_y); - HashAdd(hash, key.viewport_width); - HashAdd(hash, key.viewport_height); - HashAdd(hash, key.depth_info); - HashAdd(hash, key.surface_info); - HashAdd(hash, key.color_mask); - HashAdd(hash, key.depth_control); - HashAdd(hash, key.pa_cl_vte_cntl); - HashAdd(hash, key.primitive_type); - HashAdd(hash, key.host_vertex_shader_type); - HashAdd(hash, key.vertex_shader_hash); - HashAdd(hash, key.pixel_shader_hash); - return size_t(hash); - } -}; - -struct BuiltInTargetOverride { - uint64_t hash = 0; - SemanticTargetKey key; - bool enabled = false; -}; - -#include "skate3_ultrawide_classifier.inc" - -struct DrawFingerprintKey { - DrawFingerprint fingerprint; - - bool operator==(const DrawFingerprintKey& other) const { - const DrawFingerprint& a = fingerprint; - const DrawFingerprint& b = other.fingerprint; - return a.bucket == b.bucket && a.guest_render_object == b.guest_render_object && - a.guest_render_vtable == b.guest_render_vtable && - a.guest_render_target == b.guest_render_target && - a.vertex_shader_hash == b.vertex_shader_hash && - a.pixel_shader_hash == b.pixel_shader_hash && a.primitive_type == b.primitive_type && - a.vertex_count == b.vertex_count && a.primitive_count == b.primitive_count && - a.index_guest_base == b.index_guest_base && a.index_length == b.index_length && - std::equal(std::begin(a.vertex_fetch_address), std::end(a.vertex_fetch_address), - std::begin(b.vertex_fetch_address)) && - std::equal(std::begin(a.vertex_fetch_size), std::end(a.vertex_fetch_size), - std::begin(b.vertex_fetch_size)) && - std::equal(std::begin(a.texture_key_hash), std::end(a.texture_key_hash), - std::begin(b.texture_key_hash)) && - std::equal(std::begin(a.texture_fetch_index), std::end(a.texture_fetch_index), - std::begin(b.texture_fetch_index)); - } -}; - -struct DrawFingerprintKeyHash { - size_t operator()(const DrawFingerprintKey& key) const { - return size_t(HashDrawFingerprint(key.fingerprint)); - } -}; - -std::mutex g_mutex; -std::unordered_map g_targets; -std::unordered_map g_hash_overrides; -// std::atomic> is not available on every target toolchain -// (libc++ gates it behind a newer macOS deployment target than we target), so -// publish copy-on-write snapshots through a small mutex-guarded holder. The -// expensive map rebuild happens before store(), outside this lock, so readers -// never block on a writer's rebuild -- only the pointer swap/copy is guarded. -template -class SnapshotPtr { - public: - SnapshotPtr() = default; - SnapshotPtr(std::shared_ptr initial) : ptr_(std::move(initial)) {} - std::shared_ptr load(std::memory_order = std::memory_order_seq_cst) const { - std::lock_guard lock(mutex_); - return ptr_; - } - void store(std::shared_ptr next, std::memory_order = std::memory_order_seq_cst) { - std::lock_guard lock(mutex_); - ptr_ = std::move(next); - } - - private: - mutable std::mutex mutex_; - std::shared_ptr ptr_; -}; - -SnapshotPtr> g_hash_overrides_snapshot = - std::make_shared>(); -std::unordered_map g_semantic_overrides; -SnapshotPtr> - g_semantic_overrides_snapshot = - std::make_shared>(); -std::unordered_map g_screen_callbacks; -std::unordered_map g_screen_callback_overrides; -std::deque g_guest_store_watch_events; -std::unordered_map - g_draw_fingerprints; -std::unordered_map g_draw_fingerprint_overrides; -uint64_t g_seen_order = 0; -uint64_t g_guest_store_watch_sequence = 0; -uint64_t g_guest_store_watch_group_hash = 0; -std::filesystem::path g_guest_store_watch_log_path; -std::ofstream g_guest_store_watch_log; -std::atomic g_apply_mode = ApplyMode::kTargetList; -std::atomic g_target_overrides_loaded = false; -std::atomic g_builtin_target_overrides_active = false; -std::atomic g_target_recording_active = false; -std::atomic g_skate3_gameplay_ultrawide_latched = false; -std::atomic g_target_decision_cache_epoch = 1; -std::atomic g_guest_store_watch_enabled = false; -std::atomic g_guest_store_watch_index_base = 0; -std::atomic g_guest_store_watch_vertex_fetch_address = 0; -std::atomic g_guest_store_watch_texture_base_address = 0; -constexpr size_t kMaxGuestStoreWatchEvents = 256; -constexpr size_t kMaxDrawFingerprints = 4096; -constexpr size_t kMaxGuestFunctionStackDepth = 96; -thread_local const char* g_guest_function_stack[kMaxGuestFunctionStackDepth] = {}; -thread_local uint32_t g_guest_function_stack_depth = 0; -thread_local uint32_t g_current_draw_owner_object = 0; -thread_local uint32_t g_current_draw_owner_vtable = 0; -thread_local uint32_t g_current_draw_owner_target = 0; - -void InvalidateTargetDecisionCache() { - g_target_decision_cache_epoch.fetch_add(1, std::memory_order_acq_rel); -} - -void PublishHashOverridesSnapshotLocked() { - g_hash_overrides_snapshot.store( - std::make_shared>(g_hash_overrides), - std::memory_order_release); - InvalidateTargetDecisionCache(); -} - -void PublishSemanticOverridesSnapshotLocked() { - g_semantic_overrides_snapshot.store( - std::make_shared>( - g_semantic_overrides), - std::memory_order_release); - InvalidateTargetDecisionCache(); -} - -void InstallBuiltInSkate3ClassifierLocked() { - std::unordered_map loaded_overrides; - std::unordered_map loaded_semantic_overrides; - loaded_overrides.reserve( - sizeof(kBuiltInSkate3TargetOverrides) / sizeof(kBuiltInSkate3TargetOverrides[0])); - loaded_semantic_overrides.reserve( - sizeof(kBuiltInSkate3TargetOverrides) / sizeof(kBuiltInSkate3TargetOverrides[0])); - for (const BuiltInTargetOverride& target : kBuiltInSkate3TargetOverrides) { - loaded_overrides[target.hash] = target.enabled; - loaded_semantic_overrides[target.key] = target.enabled; - } - - g_hash_overrides = std::move(loaded_overrides); - PublishHashOverridesSnapshotLocked(); - g_semantic_overrides = std::move(loaded_semantic_overrides); - PublishSemanticOverridesSnapshotLocked(); - g_apply_mode.store(ApplyMode::kTargetList, std::memory_order_relaxed); - g_builtin_target_overrides_active.store(true, std::memory_order_release); - g_target_overrides_loaded.store(true, std::memory_order_release); -} - -void EnsureBuiltInSkate3ClassifierLoaded() { - if (g_target_overrides_loaded.load(std::memory_order_acquire)) { - return; - } - - std::lock_guard lock(g_mutex); - if (g_target_overrides_loaded.load(std::memory_order_relaxed)) { - return; - } - InstallBuiltInSkate3ClassifierLocked(); -} - -void HashAdd(uint64_t& hash, uint64_t value) { - for (size_t i = 0; i < sizeof(value); ++i) { - hash ^= uint8_t(value >> (i * 8)); - hash *= 1099511628211ull; - } -} - -void WriteHex(std::ostream& stream, uint64_t value, uint32_t width) { - stream << "0x" << std::uppercase << std::hex << std::setw(width) << std::setfill('0') << value - << std::dec << std::setfill(' '); -} - -std::string_view ScreenCallbackKindName(ScreenCallbackKind kind) { - switch (kind) { - case ScreenCallbackKind::kWidth: - return "width"; - case ScreenCallbackKind::kHeight: - return "height"; - case ScreenCallbackKind::kScreenFlag: - return "screen_flag"; - case ScreenCallbackKind::kWide: - return "wide"; - } - return "unknown"; -} - -std::optional ParseScreenCallbackKind(std::string_view name) { - if (name == "width") { - return ScreenCallbackKind::kWidth; - } - if (name == "height") { - return ScreenCallbackKind::kHeight; - } - if (name == "screen_flag") { - return ScreenCallbackKind::kScreenFlag; - } - if (name == "wide") { - return ScreenCallbackKind::kWide; - } - return std::nullopt; -} - -std::optional ParseHexString(std::string_view text) { - if (text.starts_with("0x") || text.starts_with("0X")) { - text.remove_prefix(2); - } - if (text.empty()) { - return std::nullopt; - } - - uint64_t value = 0; - for (char c : text) { - uint32_t digit = 0; - if (c >= '0' && c <= '9') { - digit = uint32_t(c - '0'); - } else if (c >= 'a' && c <= 'f') { - digit = uint32_t(c - 'a') + 10; - } else if (c >= 'A' && c <= 'F') { - digit = uint32_t(c - 'A') + 10; - } else { - return std::nullopt; - } - value = (value << 4) | digit; - } - return value; -} - -std::optional ParseU32TomlValue(toml::node_view value) { - if (auto integer = value.value()) { - if (*integer >= 0 && *integer <= UINT32_MAX) { - return uint32_t(*integer); - } - return std::nullopt; - } - if (auto text = value.value()) { - const auto parsed = ParseHexString(*text); - if (parsed && *parsed <= UINT32_MAX) { - return uint32_t(*parsed); - } - } - return std::nullopt; -} - -std::optional ParseU64TomlValue(toml::node_view value) { - if (auto integer = value.value()) { - if (*integer >= 0) { - return uint64_t(*integer); - } - return std::nullopt; - } - if (auto text = value.value()) { - return ParseHexString(*text); - } - return std::nullopt; -} - -SemanticViewportClass ClassifyViewport(uint32_t width, uint32_t height) { - if (!width || !height) { - return SemanticViewportClass::kUnknown; - } - - const double aspect = double(width) / double(height); - if (std::abs(aspect - (16.0 / 9.0)) <= 0.08) { - return SemanticViewportClass::kGameAspect; - } - if (std::abs(aspect - (32.0 / 9.0)) <= 0.16) { - return SemanticViewportClass::kDoubleWideGameAspect; - } - if (std::abs(aspect - 1.0) <= 0.03) { - return SemanticViewportClass::kSquare; - } - return SemanticViewportClass::kUnknown; -} - -uint32_t DivRound(uint64_t value, uint64_t divisor) { - if (!divisor) { - return value > UINT32_MAX ? UINT32_MAX : uint32_t(value); - } - const uint64_t result = (value + divisor / 2) / divisor; - return result > UINT32_MAX ? UINT32_MAX : uint32_t(result); -} - -std::optional NormalizeKnownViewport(uint32_t x, uint32_t y, uint32_t width, - uint32_t height) { - struct KnownViewportSize { - uint32_t width; - uint32_t height; - }; - struct KnownViewportScale { - uint32_t numerator; - uint32_t denominator; - }; - constexpr KnownViewportSize kKnownBaseSizes[] = { - {2304, 1280}, {2304, 640}, {2560, 1440}, {64, 32}, {144, 80}, - {576, 320}, {1024, 1024}, {16384, 16384}, - }; - constexpr KnownViewportScale kKnownScales[] = { - {1, 2}, {1, 1}, {3, 2}, {2, 1}, {3, 1}, {4, 1}, - }; - - for (const KnownViewportSize& base : kKnownBaseSizes) { - if (!base.width || !base.height) { - continue; - } - for (const KnownViewportScale& scale : kKnownScales) { - if (uint64_t(base.width) * scale.numerator != uint64_t(width) * scale.denominator || - uint64_t(base.height) * scale.numerator != uint64_t(height) * scale.denominator) { - continue; - } - - NormalizedViewport viewport; - viewport.viewport_class = ClassifyViewport(base.width, base.height); - viewport.x = DivRound(uint64_t(x) * scale.denominator, scale.numerator); - viewport.y = DivRound(uint64_t(y) * scale.denominator, scale.numerator); - viewport.width = base.width; - viewport.height = base.height; - return viewport; - } - } - return std::nullopt; -} - -NormalizedViewport NormalizeViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - NormalizedViewport viewport; - viewport.viewport_class = ClassifyViewport(width, height); - viewport.x = x; - viewport.y = y; - viewport.width = width; - viewport.height = height; - - if (const auto normalized_viewport = NormalizeKnownViewport(x, y, width, height)) { - return *normalized_viewport; - } - - return viewport; -} - -SemanticTargetKey MakeSemanticTargetKey(const TargetKey& key) { - SemanticTargetKey semantic_key; - const NormalizedViewport viewport = - NormalizeViewport(key.viewport_x, key.viewport_y, key.viewport_width, key.viewport_height); - semantic_key.viewport_class = viewport.viewport_class; - semantic_key.viewport_x = viewport.x; - semantic_key.viewport_y = viewport.y; - semantic_key.viewport_width = viewport.width; - semantic_key.viewport_height = viewport.height; - semantic_key.depth_info = key.depth_info; - semantic_key.surface_info = key.surface_info; - semantic_key.color_mask = key.color_mask; - semantic_key.depth_control = key.depth_control; - semantic_key.pa_cl_vte_cntl = key.pa_cl_vte_cntl; - semantic_key.primitive_type = key.primitive_type; - semantic_key.host_vertex_shader_type = key.host_vertex_shader_type; - semantic_key.vertex_shader_hash = key.vertex_shader_hash; - semantic_key.pixel_shader_hash = key.pixel_shader_hash; - return semantic_key; -} - -std::optional ParseSemanticTargetKey(toml::table& target) { - auto* viewport = target["viewport"].as_array(); - if (!viewport || viewport->size() < 4) { - return std::nullopt; - } - - const auto viewport_x = viewport->get(0) ? viewport->get(0)->value() : std::nullopt; - const auto viewport_y = viewport->get(1) ? viewport->get(1)->value() : std::nullopt; - const auto viewport_width = viewport->get(2) ? viewport->get(2)->value() : std::nullopt; - const auto viewport_height = - viewport->get(3) ? viewport->get(3)->value() : std::nullopt; - const auto depth_info = ParseU32TomlValue(target["depth_info"]); - const auto surface_info = ParseU32TomlValue(target["surface_info"]); - const auto color_mask = ParseU32TomlValue(target["color_mask"]); - const auto depth_control = ParseU32TomlValue(target["depth_control"]); - const auto pa_cl_vte_cntl = ParseU32TomlValue(target["pa_cl_vte_cntl"]); - const auto primitive_type = ParseU32TomlValue(target["primitive_type"]); - const auto host_vertex_shader_type = ParseU32TomlValue(target["host_vertex_shader_type"]); - const auto vertex_shader_hash = ParseU64TomlValue(target["vertex_shader_hash"]); - const auto pixel_shader_hash = ParseU64TomlValue(target["pixel_shader_hash"]); - if (!viewport_x || !viewport_y || !viewport_width || !viewport_height || *viewport_x < 0 || - *viewport_y < 0 || *viewport_width < 0 || *viewport_height < 0 || - *viewport_x > UINT32_MAX || *viewport_y > UINT32_MAX || *viewport_width > UINT32_MAX || - *viewport_height > UINT32_MAX || !depth_info || !surface_info || !color_mask || - !depth_control || !pa_cl_vte_cntl || !primitive_type || !host_vertex_shader_type || - !vertex_shader_hash || !pixel_shader_hash) { - return std::nullopt; - } - - SemanticTargetKey semantic_key; - const NormalizedViewport normalized_viewport = - NormalizeViewport(uint32_t(*viewport_x), uint32_t(*viewport_y), uint32_t(*viewport_width), - uint32_t(*viewport_height)); - semantic_key.viewport_class = normalized_viewport.viewport_class; - semantic_key.viewport_x = normalized_viewport.x; - semantic_key.viewport_y = normalized_viewport.y; - semantic_key.viewport_width = normalized_viewport.width; - semantic_key.viewport_height = normalized_viewport.height; - semantic_key.depth_info = *depth_info; - semantic_key.surface_info = *surface_info; - semantic_key.color_mask = *color_mask; - semantic_key.depth_control = *depth_control; - semantic_key.pa_cl_vte_cntl = *pa_cl_vte_cntl; - semantic_key.primitive_type = *primitive_type; - semantic_key.host_vertex_shader_type = *host_vertex_shader_type; - semantic_key.vertex_shader_hash = *vertex_shader_hash; - semantic_key.pixel_shader_hash = *pixel_shader_hash; - return semantic_key; -} - -std::optional FindSemanticOverride(const TargetKey& key) { - const auto overrides = g_semantic_overrides_snapshot.load(std::memory_order_acquire); - if (!overrides) { - return std::nullopt; - } - const auto semantic_key = MakeSemanticTargetKey(key); - const auto it = overrides->find(semantic_key); - if (it == overrides->end()) { - return std::nullopt; - } - return it->second; -} - -std::optional FindBuiltInFallbackOverride(const TargetKey& key) { - if (!g_builtin_target_overrides_active.load(std::memory_order_acquire)) { - return std::nullopt; - } - - const NormalizedViewport viewport = - NormalizeViewport(key.viewport_x, key.viewport_y, key.viewport_width, key.viewport_height); - if (viewport.x == 0 && viewport.y == 0 && viewport.width == 2304 && viewport.height == 640) { - return true; - } - return std::nullopt; -} - -uint64_t NowMilliseconds() { - return uint64_t(std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()); -} - -void OpenGuestStoreWatchLogLocked(bool truncate) { - if (g_guest_store_watch_log_path.empty()) { - return; - } - if (g_guest_store_watch_log.is_open()) { - return; - } - std::error_code error; - std::filesystem::create_directories(g_guest_store_watch_log_path.parent_path(), error); - g_guest_store_watch_log.open( - g_guest_store_watch_log_path, - std::ios::out | (truncate ? std::ios::trunc : std::ios::app)); - if (g_guest_store_watch_log) { - g_guest_store_watch_log - << "ms,event,seq,group,target,state,age,pkt,ib,vf0,tex,addr,value,caller_lr,function,stack,count\n"; - } -} - -void WriteGuestStoreWatchTargetsLocked(const char* event_name) { - OpenGuestStoreWatchLogLocked(false); - if (!g_guest_store_watch_log) { - return; - } - const uint32_t index_base = - g_guest_store_watch_index_base.load(std::memory_order_relaxed); - const uint32_t vertex_fetch_address = - g_guest_store_watch_vertex_fetch_address.load(std::memory_order_relaxed); - const uint32_t texture_base_address = - g_guest_store_watch_texture_base_address.load(std::memory_order_relaxed); - g_guest_store_watch_log << NowMilliseconds() << "," << event_name << ",0,"; - WriteHex(g_guest_store_watch_log, g_guest_store_watch_group_hash, 16); - g_guest_store_watch_log << ",,,0,,"; - WriteHex(g_guest_store_watch_log, index_base, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, vertex_fetch_address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, texture_base_address, 8); - g_guest_store_watch_log << ",,,,,0\n"; - g_guest_store_watch_log.flush(); -} - -void WriteGuestStoreWatchStoreLocked(const GuestStoreWatchEntry& entry) { - OpenGuestStoreWatchLogLocked(false); - if (!g_guest_store_watch_log) { - return; - } - const uint32_t index_base = - g_guest_store_watch_index_base.load(std::memory_order_relaxed); - const uint32_t vertex_fetch_address = - g_guest_store_watch_vertex_fetch_address.load(std::memory_order_relaxed); - const uint32_t texture_base_address = - g_guest_store_watch_texture_base_address.load(std::memory_order_relaxed); - g_guest_store_watch_log << NowMilliseconds() << ",store," << entry.sequence << ","; - WriteHex(g_guest_store_watch_log, g_guest_store_watch_group_hash, 16); - g_guest_store_watch_log << "," << entry.target << ",,,0,"; - WriteHex(g_guest_store_watch_log, index_base, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, vertex_fetch_address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, texture_base_address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, entry.address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, entry.value, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, entry.caller_lr, 8); - g_guest_store_watch_log << "," << entry.function << "," << entry.stack << "," << entry.count - << "\n"; - g_guest_store_watch_log.flush(); -} - -const char* DrawBucketName(DrawBucket bucket) { - switch (bucket) { - case DrawBucket::kMainColorDepth: - return "Main"; - case DrawBucket::kDepthOnly: - return "Depth"; - case DrawBucket::kCopyResolve: - return "Copy"; - case DrawBucket::kMemexport: - return "Memexport"; - case DrawBucket::kNoPixelShader: - return "NoPS"; - } - return "Unknown"; -} - -bool DrawHasWatchedTexture(const DrawFingerprint& fingerprint, uint32_t texture_base_address) { - if (texture_base_address == 0) { - return false; - } - for (uint32_t address : fingerprint.texture_base_address) { - if (address == texture_base_address) { - return true; - } - } - return false; -} - -bool DrawHasWatchedVertexFetch(const DrawFingerprint& fingerprint, - uint32_t vertex_fetch_address) { - if (vertex_fetch_address == 0) { - return true; - } - for (uint32_t address : fingerprint.vertex_fetch_address) { - if (address == vertex_fetch_address) { - return true; - } - } - return false; -} - -bool DrawMatchesGuestStoreWatchTargets(const DrawFingerprint& fingerprint) { - if (!g_guest_store_watch_enabled.load(std::memory_order_relaxed)) { - return false; - } - const uint32_t index_base = - g_guest_store_watch_index_base.load(std::memory_order_relaxed); - const uint32_t vertex_fetch_address = - g_guest_store_watch_vertex_fetch_address.load(std::memory_order_relaxed); - if (index_base != 0 && fingerprint.index_guest_base != index_base) { - return false; - } - return DrawHasWatchedVertexFetch(fingerprint, vertex_fetch_address); -} - -void WriteGuestStoreWatchDrawLocked(const DrawFingerprint& fingerprint, bool submitted, - uint64_t exact_hash) { - if (!DrawMatchesGuestStoreWatchTargets(fingerprint)) { - return; - } - OpenGuestStoreWatchLogLocked(false); - if (!g_guest_store_watch_log) { - return; - } - const uint32_t texture_base_address = - g_guest_store_watch_texture_base_address.load(std::memory_order_relaxed); - const bool has_watched_texture = DrawHasWatchedTexture(fingerprint, texture_base_address); - const bool main_textured = - has_watched_texture && fingerprint.bucket == DrawBucket::kMainColorDepth; - g_guest_store_watch_log << NowMilliseconds() << ",draw_seen,0,"; - WriteHex(g_guest_store_watch_log, g_guest_store_watch_group_hash, 16); - g_guest_store_watch_log << "," << DrawBucketName(fingerprint.bucket) << "," - << (main_textured ? "main_tex" : (has_watched_texture ? "tex" : "no_tex")) - << ",0,"; - WriteHex(g_guest_store_watch_log, fingerprint.packet_ptr, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, fingerprint.index_guest_base, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, fingerprint.vertex_fetch_address[0], 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, texture_base_address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, uint32_t(fingerprint.vertex_shader_hash), 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, uint32_t(fingerprint.pixel_shader_hash), 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, uint32_t(fingerprint.primitive_type), 8); - g_guest_store_watch_log << ",vs="; - WriteHex(g_guest_store_watch_log, fingerprint.vertex_shader_hash, 16); - g_guest_store_watch_log << " ps="; - WriteHex(g_guest_store_watch_log, fingerprint.pixel_shader_hash, 16); - g_guest_store_watch_log << " exact="; - WriteHex(g_guest_store_watch_log, exact_hash, 16); - g_guest_store_watch_log << " submitted=" << (submitted ? "1" : "0"); - g_guest_store_watch_log << ",v=" << fingerprint.vertex_count << " p=" - << fingerprint.primitive_count << ",1\n"; - g_guest_store_watch_log.flush(); -} - -std::string CurrentGuestFunctionStack() { - std::string stack; - const uint32_t depth = std::min(g_guest_function_stack_depth, 12); - for (uint32_t i = 0; i < depth; ++i) { - const uint32_t index = g_guest_function_stack_depth - depth + i; - const char* function_name = g_guest_function_stack[index]; - if (!function_name) { - continue; - } - if (!stack.empty()) { - stack += ">"; - } - stack += function_name; - } - return stack; -} - -} // namespace - -// Live value of the Skate 3 presence context (0x8001): 1 = in gameplay, -// 0 = frontend / pause menu / loading. Consumers (the native scene renderer) -// yield to the emulated output while the game is in menus. -static std::atomic g_skate3_gameplay_context_value{0}; - -void NotifySkate3GameplayContext(uint32_t user_index, uint32_t context_id, uint32_t value) { - constexpr uint32_t kSkate3GameplayContext = 0x8001; - constexpr uint32_t kSkate3GameplayValue = 1; - if (user_index != 0 || context_id != kSkate3GameplayContext) { - return; - } - g_skate3_gameplay_context_value.store(value, std::memory_order_relaxed); - if (value != kSkate3GameplayValue) { - return; - } - - bool expected = false; - if (g_skate3_gameplay_ultrawide_latched.compare_exchange_strong(expected, true)) { - REXLOG_INFO("Skate 3 ultrawide: gameplay context reached; enabling ultrawide correction"); - } -} - -bool IsSkate3GameplayUltrawideActive() { - return g_skate3_gameplay_ultrawide_latched.load(std::memory_order_relaxed); -} - -uint32_t Skate3GameplayContextValue() { - return g_skate3_gameplay_context_value.load(std::memory_order_relaxed); -} - -GuestFunctionScope::GuestFunctionScope(const char* function_name) { - if (!IsGuestStoreWatchActive()) { - return; - } - if (g_guest_function_stack_depth >= kMaxGuestFunctionStackDepth) { - return; - } - g_guest_function_stack[g_guest_function_stack_depth++] = function_name; - active_ = true; -} - -GuestFunctionScope::~GuestFunctionScope() { - if (active_ && g_guest_function_stack_depth != 0) { - --g_guest_function_stack_depth; - } -} - -bool TargetKey::operator==(const TargetKey& other) const { - return color_info == other.color_info && depth_info == other.depth_info && - surface_info == other.surface_info && viewport_x == other.viewport_x && - viewport_y == other.viewport_y && viewport_width == other.viewport_width && - viewport_height == other.viewport_height && color_mask == other.color_mask && - depth_control == other.depth_control && pa_cl_vte_cntl == other.pa_cl_vte_cntl && - primitive_type == other.primitive_type && - host_vertex_shader_type == other.host_vertex_shader_type && - vertex_shader_hash == other.vertex_shader_hash && - pixel_shader_hash == other.pixel_shader_hash; -} - -uint32_t ShadowMapCandidateLevel(const TargetKey& key) { - const bool depth_only = key.color_mask == 0 && key.pixel_shader_hash == 0; - const bool square = key.viewport_width == key.viewport_height && key.viewport_width != 0; - const bool atlas_aligned = square && (key.viewport_x == 0 || key.viewport_x % key.viewport_width == 0); - - // Skate 3 uses square 1024x1024 shadow atlas slices at X offsets that are - // multiples of the slice size. These may have either depth-only or color - // resolve/filtering passes, so don't require color_mask or pixel shader tests. - if (atlas_aligned && key.viewport_width >= 512 && key.viewport_width <= 4096) { - return 1; - } - - // Clip-disabled offscreen passes often come through as very large square - // viewports. Keep this separate because it's broader than the atlas rule. - if (depth_only && square && key.viewport_width > 4096) { - return 2; - } - - // Broad diagnostic: any depth-only pass. This may include depth pre-passes, - // so it should be used only if the narrower modes still leave shadow offsets. - if (depth_only) { - return 3; - } - - return 0; -} - -bool IsShadowMapCandidate(const TargetKey& key, uint32_t mode) { - const uint32_t level = ShadowMapCandidateLevel(key); - return level != 0 && level <= mode; -} - -uint64_t HashKey(const TargetKey& key) { - uint64_t hash = 1469598103934665603ull; - for (uint32_t color_info : key.color_info) { - HashAdd(hash, color_info); - } - HashAdd(hash, key.depth_info); - HashAdd(hash, key.surface_info); - HashAdd(hash, key.viewport_x); - HashAdd(hash, key.viewport_y); - HashAdd(hash, key.viewport_width); - HashAdd(hash, key.viewport_height); - HashAdd(hash, key.color_mask); - HashAdd(hash, key.depth_control); - HashAdd(hash, key.pa_cl_vte_cntl); - HashAdd(hash, key.primitive_type); - HashAdd(hash, key.host_vertex_shader_type); - HashAdd(hash, key.vertex_shader_hash); - HashAdd(hash, key.pixel_shader_hash); - return hash; -} - -bool ShouldApplyFast(const TargetKey& key, bool default_enabled, ApplyMode apply_mode) { - if (apply_mode == ApplyMode::kForceAll) { - return true; - } - if (apply_mode == ApplyMode::kForceNone) { - return false; - } - - struct TargetDecisionCacheEntry { - TargetKey key; - uint64_t hash = 0; - uint32_t epoch = 0; - ApplyMode apply_mode = ApplyMode::kTargetList; - bool default_enabled = false; - bool result = false; - bool valid = false; - }; - - constexpr size_t kTargetDecisionCacheSize = 8192; - thread_local std::array - target_decision_cache; - - const uint64_t key_hash = HashKey(key); - const uint32_t epoch = g_target_decision_cache_epoch.load(std::memory_order_acquire); - TargetDecisionCacheEntry& cached = - target_decision_cache[size_t(key_hash) & (kTargetDecisionCacheSize - 1)]; - if (cached.valid && cached.epoch == epoch && cached.hash == key_hash && - cached.apply_mode == apply_mode && cached.default_enabled == default_enabled && - cached.key == key) { - return cached.result; - } - - bool result = default_enabled; - const auto overrides = g_hash_overrides_snapshot.load(std::memory_order_acquire); - if (overrides) { - const auto it = overrides->find(key_hash); - if (it != overrides->end()) { - result = it->second; - cached = TargetDecisionCacheEntry{key, key_hash, epoch, apply_mode, default_enabled, result, - true}; - return result; - } - } - if (const auto semantic_override = FindSemanticOverride(key)) { - result = *semantic_override; - } else if (const auto fallback_override = FindBuiltInFallbackOverride(key)) { - result = *fallback_override; - } - cached = - TargetDecisionCacheEntry{key, key_hash, epoch, apply_mode, default_enabled, result, true}; - return result; -} - -bool ShouldApplyAndRecord(const TargetKey& key, bool default_enabled) { - EnsureBuiltInSkate3ClassifierLoaded(); - const ApplyMode apply_mode = g_apply_mode.load(std::memory_order_relaxed); - if (apply_mode == ApplyMode::kForceNone) { - return false; - } - if (apply_mode == ApplyMode::kSavedOnly || - !g_target_recording_active.load(std::memory_order_relaxed)) { - return ShouldApplyFast(key, default_enabled, apply_mode); - } - - std::lock_guard lock(g_mutex); - auto [it, inserted] = g_targets.try_emplace(key); - TargetEntry& entry = it->second; - if (inserted) { - entry.hash = HashKey(key); - entry.key = key; - if (auto override_it = g_hash_overrides.find(entry.hash); - override_it != g_hash_overrides.end()) { - entry.enabled = override_it->second; - } else if (const auto semantic_override = FindSemanticOverride(key)) { - entry.enabled = *semantic_override; - } else if (const auto fallback_override = FindBuiltInFallbackOverride(key)) { - entry.enabled = *fallback_override; - } else { - entry.enabled = default_enabled; - } - entry.default_enabled = default_enabled; - entry.shadow_candidate = ShadowMapCandidateLevel(key) != 0; - } else { - entry.default_enabled = default_enabled; - entry.shadow_candidate = ShadowMapCandidateLevel(key) != 0; - } - ++entry.draw_count; - entry.last_seen_order = ++g_seen_order; - const bool should_apply = - apply_mode == ApplyMode::kForceAll || - (apply_mode == ApplyMode::kTargetList && entry.enabled); - if (should_apply) { - ++entry.applied_draw_count; - } - return should_apply; -} - -std::vector SnapshotTargets() { - std::lock_guard lock(g_mutex); - std::vector targets; - targets.reserve(g_targets.size()); - for (const auto& [key, entry] : g_targets) { - targets.push_back(entry); - } - return targets; -} - -void SetTargetEnabled(uint64_t hash, bool enabled) { - std::lock_guard lock(g_mutex); - g_hash_overrides[hash] = enabled; - PublishHashOverridesSnapshotLocked(); - for (auto& [key, entry] : g_targets) { - if (entry.hash == hash) { - entry.enabled = enabled; - } - } -} - -void SetAllTargetsEnabled(bool enabled) { - std::lock_guard lock(g_mutex); - for (auto& [key, entry] : g_targets) { - entry.enabled = enabled; - g_hash_overrides[entry.hash] = enabled; - } - PublishHashOverridesSnapshotLocked(); -} - -void ResetTargetOverrides() { - std::lock_guard lock(g_mutex); - g_hash_overrides.clear(); - PublishHashOverridesSnapshotLocked(); - for (auto& [key, entry] : g_targets) { - entry.enabled = entry.default_enabled; - } -} - -void SetTargetRecordingActive(bool active) { - g_target_recording_active.store(active, std::memory_order_release); -} - -uint64_t HashScreenCallback(ScreenCallbackKind kind, uint32_t caller_lr) { - uint64_t hash = 1469598103934665603ull; - HashAdd(hash, uint32_t(kind)); - HashAdd(hash, caller_lr); - return hash; -} - -bool RecordScreenCallback(ScreenCallbackKind kind, uint32_t caller_lr, uint32_t value) { - if (!REXCVAR_GET(skate3_ultrawide_screen_callback_tracking)) { - return false; - } - - const uint64_t hash = HashScreenCallback(kind, caller_lr); - std::lock_guard lock(g_mutex); - auto [it, inserted] = g_screen_callbacks.try_emplace(hash); - ScreenCallbackEntry& entry = it->second; - if (inserted) { - entry.hash = hash; - entry.kind = kind; - entry.caller_lr = caller_lr; - if (auto override_it = g_screen_callback_overrides.find(hash); - override_it != g_screen_callback_overrides.end()) { - entry.enabled = override_it->second; - } - } - entry.last_value = value; - entry.last_returned_value = value; - entry.last_spoofed = false; - ++entry.call_count; - entry.last_seen_order = ++g_seen_order; - return entry.enabled; -} - -void RecordScreenCallbackResult(ScreenCallbackKind kind, uint32_t caller_lr, - uint32_t native_value, uint32_t returned_value) { - if (!REXCVAR_GET(skate3_ultrawide_screen_callback_tracking)) { - return; - } - - const uint64_t hash = HashScreenCallback(kind, caller_lr); - std::lock_guard lock(g_mutex); - auto [it, inserted] = g_screen_callbacks.try_emplace(hash); - ScreenCallbackEntry& entry = it->second; - if (inserted) { - entry.hash = hash; - entry.kind = kind; - entry.caller_lr = caller_lr; - if (auto override_it = g_screen_callback_overrides.find(hash); - override_it != g_screen_callback_overrides.end()) { - entry.enabled = override_it->second; - } - } - entry.last_value = native_value; - entry.last_returned_value = returned_value; - entry.last_spoofed = native_value != returned_value; -} - -std::vector SnapshotScreenCallbacks() { - if (!REXCVAR_GET(skate3_ultrawide_screen_callback_tracking)) { - return {}; - } - - std::lock_guard lock(g_mutex); - std::vector entries; - entries.reserve(g_screen_callbacks.size()); - for (const auto& [hash, entry] : g_screen_callbacks) { - entries.push_back(entry); - } - return entries; -} - -void SetScreenCallbackEnabled(uint64_t hash, bool enabled) { - std::lock_guard lock(g_mutex); - g_screen_callback_overrides[hash] = enabled; - if (auto it = g_screen_callbacks.find(hash); it != g_screen_callbacks.end()) { - it->second.enabled = enabled; - } -} - -void SetAllScreenCallbacksEnabled(bool enabled) { - std::lock_guard lock(g_mutex); - for (auto& [hash, entry] : g_screen_callbacks) { - entry.enabled = enabled; - g_screen_callback_overrides[hash] = enabled; - } -} - -void SetGuestStoreWatchTargets(uint32_t index_base, uint32_t vertex_fetch_address, - uint32_t texture_base_address, uint64_t group_hash) { - g_guest_store_watch_index_base.store(index_base, std::memory_order_relaxed); - g_guest_store_watch_vertex_fetch_address.store(vertex_fetch_address, std::memory_order_relaxed); - g_guest_store_watch_texture_base_address.store(texture_base_address, std::memory_order_relaxed); - g_guest_store_watch_enabled.store(index_base != 0 || vertex_fetch_address != 0 || - texture_base_address != 0, - std::memory_order_relaxed); - std::lock_guard lock(g_mutex); - g_guest_store_watch_group_hash = group_hash; - g_guest_store_watch_events.clear(); - if (g_guest_store_watch_log.is_open()) { - g_guest_store_watch_log.close(); - } - OpenGuestStoreWatchLogLocked(true); - WriteGuestStoreWatchTargetsLocked("watch_begin"); -} - -void ClearGuestStoreWatchTargets() { - { - std::lock_guard lock(g_mutex); - WriteGuestStoreWatchTargetsLocked("watch_clear"); - g_guest_store_watch_events.clear(); - if (g_guest_store_watch_log.is_open()) { - g_guest_store_watch_log.flush(); - } - } - g_guest_store_watch_enabled.store(false, std::memory_order_relaxed); - g_guest_store_watch_index_base.store(0, std::memory_order_relaxed); - g_guest_store_watch_vertex_fetch_address.store(0, std::memory_order_relaxed); - g_guest_store_watch_texture_base_address.store(0, std::memory_order_relaxed); -} - -void ClearGuestStoreWatchEvents() { - std::lock_guard lock(g_mutex); - g_guest_store_watch_events.clear(); -} - -std::vector SnapshotGuestStoreWatchEvents() { - std::lock_guard lock(g_mutex); - return {g_guest_store_watch_events.begin(), g_guest_store_watch_events.end()}; -} - -void SetGuestStoreWatchLogPath(const std::filesystem::path& path) { - std::lock_guard lock(g_mutex); - g_guest_store_watch_log_path = path; - if (g_guest_store_watch_log.is_open()) { - g_guest_store_watch_log.close(); - } -} - -void RecordGuestStoreU32(uint32_t address, uint32_t value, const char* function_name, - uint32_t caller_lr) { - if (!g_guest_store_watch_enabled.load(std::memory_order_relaxed)) { - return; - } - - const uint32_t index_base = - g_guest_store_watch_index_base.load(std::memory_order_relaxed); - const uint32_t vertex_fetch_address = - g_guest_store_watch_vertex_fetch_address.load(std::memory_order_relaxed); - const uint32_t texture_base_address = - g_guest_store_watch_texture_base_address.load(std::memory_order_relaxed); - - const char* target = nullptr; - if (index_base != 0 && value == index_base) { - target = "IB"; - } else if (vertex_fetch_address != 0 && value == vertex_fetch_address) { - target = "VF0"; - } else if (vertex_fetch_address != 0 && value == (vertex_fetch_address >> 2)) { - target = "VF0>>2"; - } else if (texture_base_address != 0 && value == texture_base_address) { - target = "Tex"; - } else if (texture_base_address != 0 && value == (texture_base_address >> 12)) { - target = "Tex>>12"; - } else { - return; - } - - const std::string current_stack = CurrentGuestFunctionStack(); - std::lock_guard lock(g_mutex); - if (!g_guest_store_watch_events.empty()) { - GuestStoreWatchEntry& previous = g_guest_store_watch_events.back(); - if (previous.target == target && previous.address == address && previous.value == value && - previous.caller_lr == caller_lr && - previous.function == (function_name ? function_name : "") && - previous.stack == current_stack) { - previous.sequence = ++g_guest_store_watch_sequence; - ++previous.count; - WriteGuestStoreWatchStoreLocked(previous); - return; - } - } - - GuestStoreWatchEntry entry; - entry.sequence = ++g_guest_store_watch_sequence; - entry.target = target; - entry.address = address; - entry.value = value; - entry.caller_lr = caller_lr; - entry.function = function_name ? function_name : ""; - entry.stack = current_stack; - g_guest_store_watch_events.push_back(std::move(entry)); - WriteGuestStoreWatchStoreLocked(g_guest_store_watch_events.back()); - while (g_guest_store_watch_events.size() > kMaxGuestStoreWatchEvents) { - g_guest_store_watch_events.pop_front(); - } -} - -void RecordDrawWatchTransition(bool present, uint64_t age, uint32_t packet_ptr, - uint32_t index_base, uint32_t vertex_fetch_address, - uint64_t group_hash) { - std::lock_guard lock(g_mutex); - OpenGuestStoreWatchLogLocked(false); - if (!g_guest_store_watch_log) { - return; - } - const uint32_t texture_base_address = - g_guest_store_watch_texture_base_address.load(std::memory_order_relaxed); - g_guest_store_watch_log << NowMilliseconds() << ",draw_transition,0,"; - WriteHex(g_guest_store_watch_log, group_hash, 16); - g_guest_store_watch_log << ",," << (present ? "present" : "missing") << ","; - if (age == UINT64_MAX) { - g_guest_store_watch_log << "n/a"; - } else { - g_guest_store_watch_log << age; - } - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, packet_ptr, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, index_base, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, vertex_fetch_address, 8); - g_guest_store_watch_log << ","; - WriteHex(g_guest_store_watch_log, texture_base_address, 8); - g_guest_store_watch_log << ",,,,,0\n"; - g_guest_store_watch_log.flush(); -} - -bool IsGuestStoreWatchActive() { - return g_guest_store_watch_enabled.load(std::memory_order_relaxed); -} - -void SetCurrentDrawOwner(uint32_t object, uint32_t vtable, uint32_t target) { - g_current_draw_owner_object = object; - g_current_draw_owner_vtable = vtable; - g_current_draw_owner_target = target; -} - -void ClearCurrentDrawOwner() { - g_current_draw_owner_object = 0; - g_current_draw_owner_vtable = 0; - g_current_draw_owner_target = 0; -} - -uint64_t HashDrawFingerprint(const DrawFingerprint& fingerprint) { - uint64_t hash = 1469598103934665603ull; - HashAdd(hash, uint32_t(fingerprint.bucket)); - HashAdd(hash, fingerprint.guest_render_object); - HashAdd(hash, fingerprint.guest_render_vtable); - HashAdd(hash, fingerprint.guest_render_target); - HashAdd(hash, fingerprint.vertex_shader_hash); - HashAdd(hash, fingerprint.pixel_shader_hash); - HashAdd(hash, fingerprint.primitive_type); - HashAdd(hash, fingerprint.vertex_count); - HashAdd(hash, fingerprint.primitive_count); - HashAdd(hash, fingerprint.index_guest_base); - HashAdd(hash, fingerprint.index_length); - for (size_t i = 0; i < DrawFingerprint::kVertexFetchCount; ++i) { - HashAdd(hash, fingerprint.vertex_fetch_address[i]); - HashAdd(hash, fingerprint.vertex_fetch_size[i]); - } - for (size_t i = 0; i < DrawFingerprint::kTextureFetchCount; ++i) { - HashAdd(hash, fingerprint.texture_key_hash[i]); - HashAdd(hash, fingerprint.texture_fetch_index[i]); - } - return hash; -} - -bool RecordDrawFingerprint(const DrawFingerprint& fingerprint) { - DrawFingerprint fingerprint_with_owner = fingerprint; - fingerprint_with_owner.guest_render_object = g_current_draw_owner_object; - fingerprint_with_owner.guest_render_vtable = g_current_draw_owner_vtable; - fingerprint_with_owner.guest_render_target = g_current_draw_owner_target; - const uint64_t hash = HashDrawFingerprint(fingerprint_with_owner); - std::lock_guard lock(g_mutex); - DrawFingerprintKey key{fingerprint_with_owner}; - auto it = g_draw_fingerprints.find(key); - if (it == g_draw_fingerprints.end()) { - if (g_draw_fingerprints.size() >= kMaxDrawFingerprints) { - return true; - } - it = g_draw_fingerprints.try_emplace(key).first; - } - DrawFingerprintEntry& entry = it->second; - if (!entry.draw_count) { - entry.fingerprint = fingerprint_with_owner; - entry.hash = hash; - } else { - entry.fingerprint.guest_render_object = fingerprint_with_owner.guest_render_object; - entry.fingerprint.guest_render_vtable = fingerprint_with_owner.guest_render_vtable; - entry.fingerprint.guest_render_target = fingerprint_with_owner.guest_render_target; - entry.fingerprint.packet_ptr = fingerprint_with_owner.packet_ptr; - entry.fingerprint.index_guest_base = fingerprint_with_owner.index_guest_base; - entry.fingerprint.index_length = fingerprint_with_owner.index_length; - std::copy(std::begin(fingerprint_with_owner.vertex_fetch_address), - std::end(fingerprint_with_owner.vertex_fetch_address), - std::begin(entry.fingerprint.vertex_fetch_address)); - std::copy(std::begin(fingerprint_with_owner.texture_key_hash), - std::end(fingerprint_with_owner.texture_key_hash), - std::begin(entry.fingerprint.texture_key_hash)); - std::copy(std::begin(fingerprint_with_owner.texture_fetch_index), - std::end(fingerprint_with_owner.texture_fetch_index), - std::begin(entry.fingerprint.texture_fetch_index)); - std::copy(std::begin(fingerprint_with_owner.texture_base_address), - std::end(fingerprint_with_owner.texture_base_address), - std::begin(entry.fingerprint.texture_base_address)); - std::copy(std::begin(fingerprint_with_owner.texture_base_length), - std::end(fingerprint_with_owner.texture_base_length), - std::begin(entry.fingerprint.texture_base_length)); - std::copy(std::begin(fingerprint_with_owner.texture_width), - std::end(fingerprint_with_owner.texture_width), - std::begin(entry.fingerprint.texture_width)); - std::copy(std::begin(fingerprint_with_owner.texture_height), - std::end(fingerprint_with_owner.texture_height), - std::begin(entry.fingerprint.texture_height)); - std::copy(std::begin(fingerprint_with_owner.texture_format), - std::end(fingerprint_with_owner.texture_format), - std::begin(entry.fingerprint.texture_format)); - } - if (auto override_it = g_draw_fingerprint_overrides.find(hash); - override_it != g_draw_fingerprint_overrides.end()) { - entry.enabled = override_it->second; - } - ++entry.draw_count; - if (entry.enabled) { - ++entry.submitted_count; - entry.vertices += fingerprint_with_owner.vertex_count; - entry.primitives += fingerprint_with_owner.primitive_count; - } else { - ++entry.skipped_count; - } - entry.last_seen_order = ++g_seen_order; - entry.last_seen_ms = NowMilliseconds(); - WriteGuestStoreWatchDrawLocked(fingerprint_with_owner, entry.enabled, hash); - return entry.enabled; -} - -std::vector SnapshotDrawFingerprints() { - std::lock_guard lock(g_mutex); - std::vector entries; - entries.reserve(g_draw_fingerprints.size()); - for (const auto& [_, entry] : g_draw_fingerprints) { - entries.push_back(entry); - } - return entries; -} - -void ClearDrawFingerprints() { - std::lock_guard lock(g_mutex); - g_draw_fingerprints.clear(); -} - -void SetDrawFingerprintEnabled(uint64_t hash, bool enabled) { - std::lock_guard lock(g_mutex); - g_draw_fingerprint_overrides[hash] = enabled; - for (auto& [key, entry] : g_draw_fingerprints) { - if (entry.hash == hash) { - entry.enabled = enabled; - } - } -} - -void SetAllDrawFingerprintsEnabled(bool enabled) { - std::lock_guard lock(g_mutex); - for (auto& [key, entry] : g_draw_fingerprints) { - entry.enabled = enabled; - g_draw_fingerprint_overrides[entry.hash] = enabled; - } -} - -void ResetDrawFingerprintOverrides() { - std::lock_guard lock(g_mutex); - g_draw_fingerprint_overrides.clear(); - for (auto& [key, entry] : g_draw_fingerprints) { - entry.enabled = true; - } -} - -ApplyMode GetApplyMode() { - return g_apply_mode.load(std::memory_order_relaxed); -} - -void SetApplyMode(ApplyMode mode) { - const ApplyMode previous = g_apply_mode.exchange(mode, std::memory_order_acq_rel); - if (previous != mode) { - InvalidateTargetDecisionCache(); - } -} - -void LoadBuiltInSkate3Classifier() { - std::lock_guard lock(g_mutex); - InstallBuiltInSkate3ClassifierLocked(); - for (auto& [key, entry] : g_targets) { - if (const auto semantic_override = FindSemanticOverride(key)) { - entry.enabled = *semantic_override; - } - } -} - -std::optional LoadTargets(const std::filesystem::path& path) { - if (path.empty() || !std::filesystem::exists(path)) { - return std::nullopt; - } - - toml::table config; - try { - config = toml::parse_file(path.string()); - } catch (const toml::parse_error&) { - return std::nullopt; - } - - std::unordered_map loaded_overrides; - std::unordered_map loaded_semantic_overrides; - std::unordered_map loaded_screen_callback_overrides; - if (auto* targets = config["target"].as_array()) { - targets->for_each([&](toml::table& target) { - const auto hash_string = target["hash"].value(); - const auto enabled = target["enabled"].value(); - if (!hash_string || !enabled) { - return; - } - const auto hash = ParseHexString(*hash_string); - if (hash) { - loaded_overrides[*hash] = *enabled; - } - if (auto semantic_key = ParseSemanticTargetKey(target)) { - loaded_semantic_overrides[*semantic_key] = *enabled; - } - }); - } - if (auto* callbacks = config["screen_callback"].as_array()) { - callbacks->for_each([&](toml::table& callback) { - const auto kind_string = callback["kind"].value(); - const auto caller_lr_string = callback["caller_lr"].value(); - const auto enabled = callback["enabled"].value(); - if (!kind_string || !caller_lr_string || !enabled) { - return; - } - const auto kind = ParseScreenCallbackKind(*kind_string); - const auto caller_lr = ParseHexString(*caller_lr_string); - if (kind && caller_lr) { - loaded_screen_callback_overrides[HashScreenCallback(*kind, uint32_t(*caller_lr))] = - *enabled; - } - }); - } - - ApplyMode loaded_mode = ApplyMode::kTargetList; - if (auto mode = config["apply_mode"].value()) { - if (*mode == "force_all") { - loaded_mode = ApplyMode::kForceAll; - } else if (*mode == "force_none") { - loaded_mode = ApplyMode::kForceNone; - } else if (*mode == "saved_only" || *mode == "hard_config") { - loaded_mode = ApplyMode::kSavedOnly; - } - } - - { - std::lock_guard lock(g_mutex); - g_hash_overrides = std::move(loaded_overrides); - PublishHashOverridesSnapshotLocked(); - g_semantic_overrides = std::move(loaded_semantic_overrides); - PublishSemanticOverridesSnapshotLocked(); - g_builtin_target_overrides_active.store(false, std::memory_order_release); - g_screen_callback_overrides = std::move(loaded_screen_callback_overrides); - g_target_overrides_loaded.store(true, std::memory_order_release); - for (auto& [key, entry] : g_targets) { - if (auto override_it = g_hash_overrides.find(entry.hash); - override_it != g_hash_overrides.end()) { - entry.enabled = override_it->second; - } else if (const auto semantic_override = FindSemanticOverride(key)) { - entry.enabled = *semantic_override; - } - } - for (auto& [hash, entry] : g_screen_callbacks) { - if (auto override_it = g_screen_callback_overrides.find(hash); - override_it != g_screen_callback_overrides.end()) { - entry.enabled = override_it->second; - } - } - } - SetApplyMode(loaded_mode); - return path; -} - -std::optional SaveTargets(const std::filesystem::path& path) { - std::ofstream stream(path, std::ios::binary | std::ios::trunc); - if (!stream) { - return std::nullopt; - } - - auto targets = SnapshotTargets(); - std::sort(targets.begin(), targets.end(), [](const TargetEntry& a, const TargetEntry& b) { - if (a.enabled != b.enabled) { - return a.enabled > b.enabled; - } - if (a.last_seen_order != b.last_seen_order) { - return a.last_seen_order > b.last_seen_order; - } - return a.hash < b.hash; - }); - - stream << "# Skate 3 ultrawide NDC target selection export\n"; - stream << "# enabled targets are the draw groups currently receiving Hor+ NDC correction\n\n"; - const ApplyMode apply_mode = GetApplyMode(); - stream << "apply_mode = \"" - << (apply_mode == ApplyMode::kForceAll - ? "force_all" - : (apply_mode == ApplyMode::kForceNone - ? "force_none" - : (apply_mode == ApplyMode::kSavedOnly ? "saved_only" : "target_list"))) - << "\"\n\n"; - for (const TargetEntry& entry : targets) { - stream << "[[target]]\n"; - stream << "enabled = " << (entry.enabled ? "true" : "false") << "\n"; - stream << "default_enabled = " << (entry.default_enabled ? "true" : "false") << "\n"; - stream << "shadow_candidate = " << (entry.shadow_candidate ? "true" : "false") << "\n"; - stream << "draw_count = " << entry.draw_count << "\n"; - stream << "applied_draw_count = " << entry.applied_draw_count << "\n"; - stream << "hash = \""; - WriteHex(stream, entry.hash, 16); - stream << "\"\n"; - stream << "color_info = ["; - for (size_t i = 0; i < entry.key.color_info.size(); ++i) { - if (i) { - stream << ", "; - } - stream << "\""; - WriteHex(stream, entry.key.color_info[i], 8); - stream << "\""; - } - stream << "]\n"; - stream << "depth_info = \""; - WriteHex(stream, entry.key.depth_info, 8); - stream << "\"\n"; - stream << "surface_info = \""; - WriteHex(stream, entry.key.surface_info, 8); - stream << "\"\n"; - stream << "viewport = [" << entry.key.viewport_x << ", " << entry.key.viewport_y << ", " - << entry.key.viewport_width << ", " << entry.key.viewport_height << "]\n"; - stream << "color_mask = \""; - WriteHex(stream, entry.key.color_mask, 8); - stream << "\"\n"; - stream << "depth_control = \""; - WriteHex(stream, entry.key.depth_control, 8); - stream << "\"\n"; - stream << "pa_cl_vte_cntl = \""; - WriteHex(stream, entry.key.pa_cl_vte_cntl, 8); - stream << "\"\n"; - stream << "primitive_type = " << entry.key.primitive_type << "\n"; - stream << "host_vertex_shader_type = " << entry.key.host_vertex_shader_type << "\n"; - stream << "vertex_shader_hash = \""; - WriteHex(stream, entry.key.vertex_shader_hash, 16); - stream << "\"\n"; - stream << "pixel_shader_hash = \""; - WriteHex(stream, entry.key.pixel_shader_hash, 16); - stream << "\"\n\n"; - } - - auto callbacks = SnapshotScreenCallbacks(); - std::sort(callbacks.begin(), callbacks.end(), - [](const ScreenCallbackEntry& a, const ScreenCallbackEntry& b) { - if (a.kind != b.kind) { - return uint32_t(a.kind) < uint32_t(b.kind); - } - return a.caller_lr < b.caller_lr; - }); - - for (const ScreenCallbackEntry& entry : callbacks) { - stream << "[[screen_callback]]\n"; - stream << "enabled = " << (entry.enabled ? "true" : "false") << "\n"; - stream << "kind = \"" << ScreenCallbackKindName(entry.kind) << "\"\n"; - stream << "caller_lr = \""; - WriteHex(stream, entry.caller_lr, 8); - stream << "\"\n"; - stream << "last_value = " << entry.last_value << "\n"; - stream << "call_count = " << entry.call_count << "\n"; - stream << "hash = \""; - WriteHex(stream, entry.hash, 16); - stream << "\"\n\n"; - } - - return path; -} - -} // namespace rex::graphics::ultrawide_debug diff --git a/src/graphics/command_processor.cpp b/src/graphics/command_processor.cpp index dec8a25..a9e0318 100644 --- a/src/graphics/command_processor.cpp +++ b/src/graphics/command_processor.cpp @@ -49,18 +49,6 @@ REXCVAR_DEFINE_BOOL(draw_resolution_scaled_texture_offsets, true, "GPU/Shader", REXCVAR_DEFINE_BOOL(occlusion_query_enable, true, "GPU", "Enable host occlusion query handling") .lifecycle(rex::cvar::Lifecycle::kHotReload); -// Skate 3 Hor+ ultrawide classifier controls shared by every graphics backend. -// Defined here (not per backend) so builds with several backends compiled in -// register them exactly once. -REXCVAR_DEFINE_BOOL(skate3_ultrawide_skip_shadow_targets, true, "Skate 3", - "Do not apply Skate 3 Hor+ NDC correction while rendering likely shadow maps") - .lifecycle(rex::cvar::Lifecycle::kHotReload); -REXCVAR_DEFINE_INT32(skate3_ultrawide_shadow_skip_mode, 2, "Skate 3", - "Shadow pass skip mode: 1 = square atlases, 2 = plus large offscreen, " - "3 = plus all depth-only") - .range(1, 3) - .lifecycle(rex::cvar::Lifecycle::kHotReload); - REXCVAR_DEFINE_STRING(readback_resolve, "none", "GPU", "Controls CPU readback of render-to-texture resolve results.\n" " none: Disable readback (default)\n" diff --git a/src/graphics/d3d12/command_processor.cpp b/src/graphics/d3d12/command_processor.cpp index 44995da..df5d087 100644 --- a/src/graphics/d3d12/command_processor.cpp +++ b/src/graphics/d3d12/command_processor.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -58,18 +57,6 @@ REXCVAR_DEFINE_BOOL(d3d12_gpu_timestamp_buckets, false, "GPU/D3D12", "the breakdown in the FPS overlay and a periodic profile log") .lifecycle(rex::cvar::Lifecycle::kHotReload); -// Defined in graphics/command_processor.cpp, shared with the Vulkan backend. -REXCVAR_DECLARE(bool, skate3_ultrawide_skip_shadow_targets); -REXCVAR_DECLARE(int32_t, skate3_ultrawide_shadow_skip_mode); -REXCVAR_DEFINE_BOOL(skate3_ultrawide_fake_occlusion_queries, false, "Skate 3", - "Return positive visibility query results while Skate 3 Hor+ ultrawide is " - "active so side-of-frame assets remain resident") - .lifecycle(rex::cvar::Lifecycle::kHotReload); -REXCVAR_DEFINE_INT32(skate3_ultrawide_fake_occlusion_sample_count, 1000, "Skate 3", - "Sample count returned by Skate 3 ultrawide visibility query faking") - .range(1, 1000000) - .lifecycle(rex::cvar::Lifecycle::kHotReload); - namespace rex::graphics::d3d12 { namespace { @@ -82,154 +69,6 @@ bool IsGameplayStateActive(const system::KernelState* kernel_state) { return gameplay_context && *gameplay_context == 1; } -struct Skate3UltrawideDrawState { - bool correction_active = false; - bool skip_shadow_targets = false; - uint32_t shadow_skip_mode = 1; - float hor_plus_scale = 1.0f; -}; - -double GetSkate3UltrawideTargetAspect(const ui::Presenter* presenter = nullptr) { - if (!rex::cvar::Query("skate3_ultrawide") || - !ultrawide_debug::IsSkate3GameplayUltrawideActive()) { - return 0.0; - } - - constexpr double kBaseAspect = 16.0 / 9.0; - uint32_t surface_width = 0; - uint32_t surface_height = 0; - if (presenter && presenter->GetSurfacePaintConnectionSize(surface_width, surface_height) && - surface_width && surface_height) { - const double surface_aspect = double(surface_width) / double(surface_height); - if (surface_aspect > kBaseAspect + 0.01) { - return std::clamp(surface_aspect, kBaseAspect, 8.0); - } - } - - const double target_aspect = rex::cvar::Query("skate3_ultrawide_target_aspect"); - return target_aspect > kBaseAspect + 0.01 ? std::clamp(target_aspect, kBaseAspect, 8.0) : 0.0; -} - -void ApplySkate3UltrawidePresenterAspect(const ui::Presenter* presenter, uint32_t& display_width, - uint32_t& display_height) { - const double target_aspect = GetSkate3UltrawideTargetAspect(presenter); - if (target_aspect <= 0.0) { - return; - } - - rex::cvar::SetFlagByName("skate3_ultrawide_target_aspect", std::to_string(target_aspect)); - - display_height = - uint32_t(std::clamp(rex::cvar::Query("skate3_ultrawide_base_height"), 480, 2160)); - display_width = - uint32_t(std::clamp(target_aspect * double(display_height) + 0.5, 1.0, 4095.0)); -} - -float ComputeSkate3UltrawideHorPlusScale() { - const double manual_scale = rex::cvar::Query("skate3_ultrawide_hor_plus_scale"); - if (manual_scale > 0.0) { - return float(std::clamp(manual_scale, 0.25, 4.0)); - } - - constexpr double kBaseAspect = 16.0 / 9.0; - double aspect = GetSkate3UltrawideTargetAspect(); - if (aspect <= 0.0) { - const int32_t display_width = REXCVAR_GET(video_mode_width); - const int32_t display_height = REXCVAR_GET(video_mode_height); - if (display_width <= 0 || display_height <= 0) { - return 1.0f; - } - aspect = double(display_width) / double(display_height); - } - - if (aspect <= kBaseAspect + 0.01f) { - return 1.0f; - } - - return float(std::clamp(kBaseAspect / aspect, 0.25, 1.0)); -} - -Skate3UltrawideDrawState QuerySkate3UltrawideDrawState() { - Skate3UltrawideDrawState state; - state.correction_active = rex::cvar::Query("skate3_ultrawide") && - rex::cvar::Query("skate3_ultrawide_hor_plus") && - ultrawide_debug::IsSkate3GameplayUltrawideActive() && - !rex::cvar::Query("skate3_ultrawide_disable_ndc_correction"); - if (!state.correction_active) { - return state; - } - - state.hor_plus_scale = ComputeSkate3UltrawideHorPlusScale(); - state.skip_shadow_targets = REXCVAR_GET(skate3_ultrawide_skip_shadow_targets); - state.shadow_skip_mode = uint32_t(REXCVAR_GET(skate3_ultrawide_shadow_skip_mode)); - return state; -} - -Skate3UltrawideDrawState SnapshotSkate3UltrawideDrawState(uint64_t frame) { - constexpr uint64_t kUncachedFrame = ~uint64_t(0); - if (frame == kUncachedFrame) { - return QuerySkate3UltrawideDrawState(); - } - - thread_local uint64_t cached_frame = 0; - thread_local Skate3UltrawideDrawState cached_state; - if (cached_frame != frame) { - cached_state = QuerySkate3UltrawideDrawState(); - cached_frame = frame; - } - return cached_state; -} - -bool ShouldFakeSkate3UltrawideOcclusionQueries() { - const Skate3UltrawideDrawState state = SnapshotSkate3UltrawideDrawState(~uint64_t(0)); - return rex::cvar::Query("skate3_ultrawide_fake_occlusion_queries") && - state.correction_active && state.hor_plus_scale < 0.999f; -} - -bool IsSkate3UltrawideDisplayViewport(const draw_util::ViewportInfo& viewport_info) { - const uint32_t width = viewport_info.xy_extent[0]; - const uint32_t height = viewport_info.xy_extent[1]; - if (!width || !height) { - return false; - } - - const uint64_t scaled_width = uint64_t(width) * 9; - const uint64_t scaled_height = uint64_t(height) * 16; - const uint64_t difference = - scaled_width > scaled_height ? scaled_width - scaled_height : scaled_height - scaled_width; - return difference <= scaled_height / 20; -} - -bool ShouldApplySkate3UltrawideHorPlus( - const ultrawide_debug::TargetKey& target_key, bool primitive_polygonal, - const draw_util::ViewportInfo& viewport_info, reg::RB_DEPTHCONTROL normalized_depth_control, - const Skate3UltrawideDrawState& ultrawide_state) { - if (!ultrawide_state.correction_active) { - return false; - } - - const bool default_enabled = primitive_polygonal && normalized_depth_control.z_enable && - IsSkate3UltrawideDisplayViewport(viewport_info); - const bool selected = ultrawide_debug::ShouldApplyAndRecord(target_key, default_enabled); - if (selected && ultrawide_state.skip_shadow_targets && - ultrawide_debug::IsShadowMapCandidate(target_key, ultrawide_state.shadow_skip_mode)) { - return false; - } - return selected; -} - -// Frame-cached read of the cross-TU draw trace flag - the registry Query is a -// mutex + string hash, too expensive to run for every draw. -bool QuerySkate3UltrawideTraceDraws(uint64_t frame) { - thread_local uint64_t cached_frame = UINT64_MAX; - thread_local bool cached_value = false; - if (cached_frame != frame) { - cached_value = rex::cvar::Query("skate3_ultrawide_trace_draws"); - cached_frame = frame; - } - return cached_value; -} - #ifdef REXGLUE_ENABLE_PERF_COUNTERS class ScopedDrawStageTimer { public: @@ -419,19 +258,6 @@ bool D3D12CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(memory::RingBuffe return true; }; - if (ShouldFakeSkate3UltrawideOcclusionQueries()) { - if (active_occlusion_query_.valid) { - uint32_t host_index = active_occlusion_query_.host_index; - active_occlusion_query_ = {}; - if (occlusion_query_heap_ && BeginSubmission(true)) { - deferred_command_list_.D3DEndQuery(occlusion_query_heap_.Get(), D3D12_QUERY_TYPE_OCCLUSION, - host_index); - EndSubmission(false); - } - } - return write_fake_result(REXCVAR_GET(skate3_ultrawide_fake_occlusion_sample_count)); - } - // Native guest-output renderer active: the framebuffer-sized draws between // query begin/end are suppressed, so a real host occlusion query reports 0 // samples. The game gates world rendering on these results; event-ad @@ -2339,13 +2165,17 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu kernel::xboxkrnl::VdQueryVideoMode(&video_mode); uint32_t display_width = std::max(uint32_t(1), uint32_t(video_mode.display_width)); uint32_t display_height = std::max(uint32_t(1), uint32_t(video_mode.display_height)); - ApplySkate3UltrawidePresenterAspect(presenter, display_width, display_height); + // Ultrawide: while the native renderer is serving frames, allocate the + // guest output at the wide display aspect; emulated frames keep the + // frontbuffer aspect and present pillarboxed. + const bool native_wide_output = ApplyNativeGuestOutputWideAspect( + guest_output_width, guest_output_height, display_width, display_height); presenter->RefreshGuestOutput( guest_output_width, guest_output_height, display_width, display_height, [this, &swap_texture_srv_desc, frontbuffer_format, swap_texture_resource, guest_output_width, - guest_output_height, display_width, - display_height](ui::Presenter::GuestOutputRefreshContext& context) -> bool { + guest_output_height, display_width, display_height, + native_wide_output](ui::Presenter::GuestOutputRefreshContext& context) -> bool { const ui::d3d12::D3D12Provider& provider = GetD3D12Provider(); ID3D12Device* device = provider.GetDevice(); @@ -2427,6 +2257,12 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu EndSubmission(true); return true; } + if (native_wide_output) { + // The renderer yielded but the output was sized wide for it; the + // emulated blit writes frontbuffer-aspect content, so keep the + // previously presented image and resize on the next refresh. + return false; + } } // Upload the new gamma ramp, using the upload buffer for the current @@ -3146,71 +2982,6 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, uint3 // Must not call anything that may change the primitive topology from now on! const uint32_t host_draw_vertex_count = primitive_processing_result.host_draw_vertex_count; - const uint32_t skate3_ultrawide_draw_primitive_count = draw_util::EstimatePrimitiveCount( - primitive_processing_result.host_primitive_type, host_draw_vertex_count); - bool skate3_ultrawide_submit_draw = true; - // Frame-cached: the registry Query takes the registry mutex and hashes the - // name, far too expensive for a per-draw debug-flag check. - if (QuerySkate3UltrawideTraceDraws(frame_current_)) { - ultrawide_debug::DrawFingerprint fingerprint; - fingerprint.bucket = - memexport_used - ? ultrawide_debug::DrawBucket::kMemexport - : (edram_mode == xenos::EdramMode::kDepthOnly - ? ultrawide_debug::DrawBucket::kDepthOnly - : (pixel_shader == nullptr ? ultrawide_debug::DrawBucket::kNoPixelShader - : ultrawide_debug::DrawBucket::kMainColorDepth)); - fingerprint.vertex_shader_hash = vertex_shader->ucode_data_hash(); - fingerprint.pixel_shader_hash = pixel_shader ? pixel_shader->ucode_data_hash() : 0; - fingerprint.primitive_type = uint32_t(primitive_processing_result.host_primitive_type); - fingerprint.vertex_count = host_draw_vertex_count; - fingerprint.primitive_count = skate3_ultrawide_draw_primitive_count; - fingerprint.packet_ptr = current_packet_ptr_; - if (index_buffer_info) { - fingerprint.index_guest_base = index_buffer_info->guest_base; - fingerprint.index_length = uint32_t(index_buffer_info->length); - } - const Shader::ConstantRegisterMap& constant_map = vertex_shader->constant_register_map(); - size_t fetch_count = 0; - for (uint32_t i = 0; i < rex::countof(constant_map.vertex_fetch_bitmap); ++i) { - uint32_t fetch_bits = constant_map.vertex_fetch_bitmap[i]; - uint32_t bit = 0; - while (fetch_count < ultrawide_debug::DrawFingerprint::kVertexFetchCount && - rex::bit_scan_forward(fetch_bits, &bit)) { - fetch_bits &= ~(uint32_t(1) << bit); - xenos::xe_gpu_vertex_fetch_t fetch = regs.GetVertexFetch(i * 32 + bit); - fingerprint.vertex_fetch_address[fetch_count] = fetch.address << 2; - fingerprint.vertex_fetch_size[fetch_count] = fetch.size << 2; - ++fetch_count; - } - if (fetch_count >= ultrawide_debug::DrawFingerprint::kVertexFetchCount) { - break; - } - } - size_t texture_count = 0; - uint32_t texture_bits = used_texture_mask; - uint32_t texture_index = 0; - while (texture_count < ultrawide_debug::DrawFingerprint::kTextureFetchCount && - rex::bit_scan_forward(texture_bits, &texture_index)) { - texture_bits &= ~(UINT32_C(1) << texture_index); - TextureCache::DebugActiveTextureBinding binding; - if (!texture_cache_->DebugGetActiveTextureBinding(texture_index, binding)) { - continue; - } - fingerprint.texture_key_hash[texture_count] = binding.key_hash; - fingerprint.texture_fetch_index[texture_count] = binding.fetch_index; - fingerprint.texture_base_address[texture_count] = binding.base_address; - fingerprint.texture_base_length[texture_count] = binding.base_length; - fingerprint.texture_width[texture_count] = binding.width; - fingerprint.texture_height[texture_count] = binding.height; - fingerprint.texture_format[texture_count] = binding.format; - ++texture_count; - } - skate3_ultrawide_submit_draw = ultrawide_debug::RecordDrawFingerprint(fingerprint); - } - if (!skate3_ultrawide_submit_draw) { - return true; - } const rex::perf::DrawBucket draw_bucket = memexport_used ? rex::perf::DrawBucket::kMemexport @@ -4637,37 +4408,10 @@ void D3D12CommandProcessor::UpdateSystemConstantValues( } // Conversion to Direct3D 12 normalized device coordinates. - ultrawide_debug::TargetKey skate3_ultrawide_target_key; - for (uint32_t i = 0; i < 4; ++i) { - skate3_ultrawide_target_key.color_info[i] = color_infos[i].value; - } - skate3_ultrawide_target_key.depth_info = rb_depth_info.value; - skate3_ultrawide_target_key.surface_info = rb_surface_info.value; - skate3_ultrawide_target_key.viewport_x = viewport_info.xy_offset[0]; - skate3_ultrawide_target_key.viewport_y = viewport_info.xy_offset[1]; - skate3_ultrawide_target_key.viewport_width = viewport_info.xy_extent[0]; - skate3_ultrawide_target_key.viewport_height = viewport_info.xy_extent[1]; - skate3_ultrawide_target_key.color_mask = normalized_color_mask; - skate3_ultrawide_target_key.depth_control = normalized_depth_control.value; - skate3_ultrawide_target_key.pa_cl_vte_cntl = pa_cl_vte_cntl.value; - skate3_ultrawide_target_key.primitive_type = uint32_t(vgt_draw_initiator.prim_type); - skate3_ultrawide_target_key.host_vertex_shader_type = uint32_t(host_vertex_shader_type); - skate3_ultrawide_target_key.vertex_shader_hash = vertex_shader_hash; - skate3_ultrawide_target_key.pixel_shader_hash = pixel_shader_hash; - const Skate3UltrawideDrawState skate3_ultrawide_state = - SnapshotSkate3UltrawideDrawState(frame_current_); - const bool skate3_ultrawide_hor_plus_draw = ShouldApplySkate3UltrawideHorPlus( - skate3_ultrawide_target_key, primitive_polygonal, viewport_info, normalized_depth_control, - skate3_ultrawide_state); - const float skate3_ultrawide_hor_plus_scale = - skate3_ultrawide_hor_plus_draw ? skate3_ultrawide_state.hor_plus_scale : 1.0f; for (uint32_t i = 0; i < 3; ++i) { - const float ndc_scale = - i == 0 ? viewport_info.ndc_scale[i] * skate3_ultrawide_hor_plus_scale - : viewport_info.ndc_scale[i]; - dirty |= system_constants_.ndc_scale[i] != ndc_scale; + dirty |= system_constants_.ndc_scale[i] != viewport_info.ndc_scale[i]; dirty |= system_constants_.ndc_offset[i] != viewport_info.ndc_offset[i]; - system_constants_.ndc_scale[i] = ndc_scale; + system_constants_.ndc_scale[i] = viewport_info.ndc_scale[i]; system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i]; } diff --git a/src/graphics/native_guest_renderer.cpp b/src/graphics/native_guest_renderer.cpp index 4eb66f5..dd39116 100644 --- a/src/graphics/native_guest_renderer.cpp +++ b/src/graphics/native_guest_renderer.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -114,6 +115,41 @@ bool IsNativeGuestOutputActive() { return g_native_output_active.load(std::memory_order_relaxed); } +namespace { +std::atomic g_wide_aspect{0.0}; +} // namespace + +void SetNativeGuestOutputWideAspect(double aspect) { + g_wide_aspect.store(aspect > 0.0 ? aspect : 0.0, std::memory_order_relaxed); +} + +double GetNativeGuestOutputWideAspect() { + return g_wide_aspect.load(std::memory_order_relaxed); +} + +bool ApplyNativeGuestOutputWideAspect(uint32_t& guest_output_width, uint32_t guest_output_height, + uint32_t& display_width, uint32_t& display_height) { + const double aspect = g_wide_aspect.load(std::memory_order_relaxed); + if (aspect <= 0.0 || !guest_output_width || !guest_output_height || + !HasNativeGuestOutputRenderer() || + !g_native_output_active.load(std::memory_order_relaxed)) { + return false; + } + // Even width keeps every half-resolution derived target (bloom, SSAO, + // menu snapshots) an exact divide. + uint32_t wide_width = + uint32_t(std::clamp(aspect * double(guest_output_height) + 0.5, 1.0, 16384.0)) & ~1u; + if (wide_width <= guest_output_width) { + return false; + } + guest_output_width = wide_width; + // The display aspect is the actual pixel aspect: the presenter maps the + // wide image edge-to-edge on a matching window. + display_width = guest_output_width; + display_height = guest_output_height; + return true; +} + bool ShouldSuppressEmulatedDraws() { return REXCVAR_GET(native_render_suppress_emulated_draws) && g_native_output_active.load(std::memory_order_relaxed); diff --git a/src/graphics/pipeline/texture/cache.cpp b/src/graphics/pipeline/texture/cache.cpp index acd9e29..33a381e 100644 --- a/src/graphics/pipeline/texture/cache.cpp +++ b/src/graphics/pipeline/texture/cache.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -108,25 +107,6 @@ REXCVAR_DEFINE_INT32(vulkan_debug_log_team_profile_background_bindings_remaining .lifecycle(rex::cvar::Lifecycle::kHotReload) .debug_only(); -REXCVAR_DEFINE_INT32(skate3_ultrawide_texture_trace_remaining, 0, "Skate 3/Ultrawide", - "Log this many Skate 3 texture lifecycle events for ultrawide pop-in tracing") - .range(0, 1000000) - .lifecycle(rex::cvar::Lifecycle::kHotReload); - -REXCVAR_DEFINE_BOOL(skate3_ultrawide_texture_trace_bind_keys, false, "Skate 3/Ultrawide", - "Include high-frequency texture bind-key churn in ultrawide texture tracing") - .lifecycle(rex::cvar::Lifecycle::kHotReload); - -REXCVAR_DEFINE_BOOL(skate3_ultrawide_texture_trace_scaled_resolve, false, "Skate 3/Ultrawide", - "Include scaled render-to-texture resolve targets in ultrawide texture tracing") - .lifecycle(rex::cvar::Lifecycle::kHotReload); - -REXCVAR_DEFINE_BOOL(skate3_ultrawide_ignore_streamer_texture_invalidations, false, - "Skate 3/Ultrawide", - "Diagnostic: keep host textures resident when guest streamer writes from " - "82CD4300 invalidate watched texture pages") - .lifecycle(rex::cvar::Lifecycle::kHotReload); - REXCVAR_DEFINE_BOOL(pre_mask_resolve_l2_block, true, "GPU", "Pre-mask scaled resolve L2 blocks to the write range before iterating"); @@ -344,9 +324,6 @@ void TextureCache::CompletedSubmissionUpdated(uint64_t completed_submission_inde assert_true(found_texture_it != textures_.end()); if (found_texture_it != textures_.end()) { assert_true(found_texture_it->second.get() == texture); - DebugLogSkate3UltrawideTextureTrace(limit_hard_exceeded ? "destroy-hard-limit" - : "destroy-soft-limit", - UINT32_MAX, texture->key(), texture); textures_.erase(found_texture_it); // `texture` is invalid now. } @@ -517,11 +494,6 @@ bool TextureCache::CommitPreparedTextureLoad(const PendingTextureLoad& pending_l // not up to date anymore. texture.MakeUpToDateAndWatch(global_critical_region_.Acquire()); texture.LogAction("Loaded"); - DebugLogSkate3UltrawideTextureTrace(pending_load.load_base - ? (pending_load.load_mips ? "loaded-base-mips" - : "loaded-base") - : "loaded-mips", - UINT32_MAX, texture.key(), &texture); DebugLogTeamProfileBackgroundTextureCandidate("loaded", UINT32_MAX, texture); return true; @@ -598,7 +570,6 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) { if (!binding.key.is_valid) { if (old_key.is_valid) { bindings_changed |= index_bit; - DebugLogSkate3UltrawideTextureTrace("unbind-invalid-fetch", index, old_key); } binding.Reset(); continue; @@ -660,15 +631,6 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) { if (load_signed_data) { queue_pending_texture_load(binding.texture_signed); } - if (key_changed) { - DebugLogSkate3UltrawideTextureTrace("bind-key", index, binding.key, - binding.texture ? binding.texture - : binding.texture_signed); - } else if (bindings_changed & index_bit) { - DebugLogSkate3UltrawideTextureTrace("bind-view", index, binding.key, - binding.texture ? binding.texture - : binding.texture_signed); - } } COUNT_profile_set("gpu/texture_cache/request_textures_pending_load_count", @@ -901,90 +863,6 @@ bool TextureCache::DebugFindTeamProfileBackgroundBinding(uint32_t used_texture_m return false; } -bool TextureCache::DebugConsumeSkate3UltrawideTextureTraceLog() { - int32_t remaining = REXCVAR_GET(skate3_ultrawide_texture_trace_remaining); - if (remaining <= 0) { - return false; - } - REXCVAR_SET(skate3_ultrawide_texture_trace_remaining, remaining - 1); - return true; -} - -void TextureCache::DebugLogSkate3UltrawideTextureTrace(const char* action, uint32_t fetch_index, - const TextureKey& key, - const Texture* texture) { - const bool is_bind_action = std::string_view(action).starts_with("bind-"); - if (is_bind_action && !REXCVAR_GET(skate3_ultrawide_texture_trace_bind_keys)) { - return; - } - if (key.is_valid && key.scaled_resolve && - !REXCVAR_GET(skate3_ultrawide_texture_trace_scaled_resolve)) { - return; - } - - if (!key.is_valid) { - if (!DebugConsumeSkate3UltrawideTextureTraceLog()) { - return; - } - REXGPU_WARN("Skate3 texture trace: action={}, submission={}, fetch={}, invalid-key", action, - current_submission_index_, fetch_index == UINT32_MAX ? -1 : int32_t(fetch_index)); - return; - } - - const FormatInfo* format_info = FormatInfo::Get(key.format); - const uint64_t key_hash = TextureKey::Hasher{}(key); - uint32_t guest_base_size = 0; - uint32_t guest_mips_size = 0; - if (texture) { - guest_base_size = texture->GetGuestBaseSize(); - guest_mips_size = texture->GetGuestMipsSize(); - } else { - const texture_util::TextureGuestLayout fallback_layout = key.GetGuestLayout(); - guest_base_size = fallback_layout.base.level_data_extent_bytes; - guest_mips_size = fallback_layout.mips_total_extent_bytes; - } - const uint32_t host_width = key.GetWidth() * (key.scaled_resolve ? draw_resolution_scale_x_ : 1); - const uint32_t host_height = - key.GetHeight() * (key.scaled_resolve ? draw_resolution_scale_y_ : 1); - - if (!DebugConsumeSkate3UltrawideTextureTraceLog()) { - return; - } - - const auto& invalidation_diagnostics = - rex::memory::GetPhysicalMemoryInvalidationDiagnostics(); - if (std::string_view(action).starts_with("invalidate-") && invalidation_diagnostics.valid) { - REXGPU_WARN( - "Skate3 texture trace: action={}, submission={}, fetch={}, key={:016X}, scaled={}, {} " - "{}x{}x{} host={}x{} {}, base={:08X}+{:X}, mips={:08X}+{:X}, mip_max={}, pitch={}, " - "outdated={:X}, host_mem_kb={}, write_va={:08X}, write_phys={:08X}+{:X}, " - "thread={:08X}, lr={:08X}, stack={:08X}, exact={}, unprotect={}", - action, current_submission_index_, fetch_index == UINT32_MAX ? -1 : int32_t(fetch_index), - key_hash, bool(key.scaled_resolve), key.tiled ? "tiled" : "linear", key.GetWidth(), - key.GetHeight(), key.GetDepthOrArraySize(), host_width, host_height, - format_info ? format_info->name : "unknown", key.base_page << 12, guest_base_size, - key.mip_page << 12, guest_mips_size, key.mip_max_level, key.pitch << 5, - texture ? texture->outdated_mask() : 0, - texture ? uint32_t((texture->GetHostMemoryUsage() + 1023) >> 10) : 0, - invalidation_diagnostics.virtual_address, invalidation_diagnostics.physical_address, - invalidation_diagnostics.length, invalidation_diagnostics.thread_id, - invalidation_diagnostics.lr, invalidation_diagnostics.stack, - invalidation_diagnostics.exact_range, invalidation_diagnostics.unprotect); - return; - } - REXGPU_WARN( - "Skate3 texture trace: action={}, submission={}, fetch={}, key={:016X}, scaled={}, {} " - "{}x{}x{} host={}x{} {}, base={:08X}+{:X}, mips={:08X}+{:X}, mip_max={}, pitch={}, " - "outdated={:X}, host_mem_kb={}", - action, current_submission_index_, fetch_index == UINT32_MAX ? -1 : int32_t(fetch_index), - key_hash, bool(key.scaled_resolve), key.tiled ? "tiled" : "linear", key.GetWidth(), - key.GetHeight(), key.GetDepthOrArraySize(), host_width, host_height, - format_info ? format_info->name : "unknown", key.base_page << 12, guest_base_size, - key.mip_page << 12, guest_mips_size, key.mip_max_level, key.pitch << 5, - texture ? texture->outdated_mask() : 0, - texture ? uint32_t((texture->GetHostMemoryUsage() + 1023) >> 10) : 0); -} - void TextureCache::DebugLogTeamProfileBackgroundTextureCandidate(const char* action, uint32_t fetch_index, const Texture& texture) { @@ -1380,26 +1258,6 @@ void TextureCache::Texture::MarkAsUsed() { void TextureCache::Texture::WatchCallback( [[maybe_unused]] const std::unique_lock& global_lock, bool is_mip, bool invalidated_by_gpu) { - const auto& invalidation_diagnostics = - rex::memory::GetPhysicalMemoryInvalidationDiagnostics(); - const bool ignore_skate3_streamer_invalidation = - !invalidated_by_gpu && REXCVAR_GET(skate3_ultrawide_ignore_streamer_texture_invalidations) && - invalidation_diagnostics.valid && invalidation_diagnostics.lr == 0x82CD4300; - if (ignore_skate3_streamer_invalidation) { - texture_cache().DebugLogSkate3UltrawideTextureTrace( - is_mip ? "suppress-streamer-mips" : "suppress-streamer-base", UINT32_MAX, key(), this); - if (is_mip) { - mips_watch_handle_ = nullptr; - } else { - base_watch_handle_ = nullptr; - } - return; - } - - texture_cache().DebugLogSkate3UltrawideTextureTrace( - invalidated_by_gpu ? (is_mip ? "invalidate-gpu-mips" : "invalidate-gpu-base") - : (is_mip ? "invalidate-cpu-mips" : "invalidate-cpu-base"), - UINT32_MAX, key(), this); texture_cache().DebugLogTeamProfileBackgroundTextureCandidate( invalidated_by_gpu ? (is_mip ? "invalidate-gpu-mips" : "invalidate-gpu-base") : (is_mip ? "invalidate-cpu-mips" : "invalidate-cpu-base"), @@ -1427,11 +1285,6 @@ void TextureCache::WatchCallback(const std::unique_lock& g void TextureCache::DestroyAllTextures(bool from_destructor) { ResetTextureBindings(from_destructor); - if (!from_destructor) { - for (const auto& [key, texture] : textures_) { - DebugLogSkate3UltrawideTextureTrace("destroy-clear", UINT32_MAX, key, texture.get()); - } - } textures_.clear(); COUNT_profile_set("gpu/texture_cache/textures", 0); } @@ -1554,7 +1407,6 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) { } COUNT_profile_set("gpu/texture_cache/textures", textures_.size()); texture->LogAction("Created"); - DebugLogSkate3UltrawideTextureTrace("create", UINT32_MAX, texture->key(), texture); DebugLogTeamProfileBackgroundTextureCandidate("create", UINT32_MAX, *texture); return texture; } diff --git a/src/graphics/vulkan/command_processor.cpp b/src/graphics/vulkan/command_processor.cpp index 7cb7a5e..69863ce 100644 --- a/src/graphics/vulkan/command_processor.cpp +++ b/src/graphics/vulkan/command_processor.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -161,15 +160,6 @@ REXCVAR_DEFINE_INT32(vulkan_gpu_clock_log_interval_frames, 0, "GPU/Vulkan", .debug_only() .lifecycle(rex::cvar::Lifecycle::kHotReload); -// The shared Skate 3 Hor+ classifier cvars (skate3_ultrawide_skip_shadow_targets, -// skate3_ultrawide_shadow_skip_mode) live in graphics/command_processor.cpp so -// every backend sees one registration; the queries below resolve them by name. -REXCVAR_DEFINE_BOOL(skate3_ultrawide_skip_screen_space_targets, false, "Skate 3", - "Exclude screen-space/orthographic (pre-divided XY) passes from Skate 3 Hor+ " - "NDC correction. Off by default so the Vulkan default classifier matches the " - "Direct3D 12 build; enable if menu/HUD fragments appear in the side areas") - .lifecycle(rex::cvar::Lifecycle::kHotReload); - namespace rex::graphics::vulkan { namespace { @@ -243,160 +233,6 @@ bool IsGameplayStateActive(const system::KernelState* kernel_state) { return gameplay_context && *gameplay_context == 1; } -struct Skate3UltrawideDrawState { - bool correction_active = false; - bool skip_shadow_targets = false; - bool skip_screen_space_targets = false; - uint32_t shadow_skip_mode = 1; - float hor_plus_scale = 1.0f; -}; - -double GetSkate3UltrawideTargetAspect(const ui::Presenter* presenter = nullptr) { - if (!rex::cvar::Query("skate3_ultrawide") || - !ultrawide_debug::IsSkate3GameplayUltrawideActive()) { - return 0.0; - } - - constexpr double kBaseAspect = 16.0 / 9.0; - uint32_t surface_width = 0; - uint32_t surface_height = 0; - if (presenter && presenter->GetSurfacePaintConnectionSize(surface_width, surface_height) && - surface_width && surface_height) { - const double surface_aspect = double(surface_width) / double(surface_height); - if (surface_aspect > kBaseAspect + 0.01) { - return std::clamp(surface_aspect, kBaseAspect, 8.0); - } - } - - const double target_aspect = rex::cvar::Query("skate3_ultrawide_target_aspect"); - return target_aspect > kBaseAspect + 0.01 ? std::clamp(target_aspect, kBaseAspect, 8.0) : 0.0; -} - -void ApplySkate3UltrawidePresenterAspect(const ui::Presenter* presenter, uint32_t& display_width, - uint32_t& display_height) { - const double target_aspect = GetSkate3UltrawideTargetAspect(presenter); - if (target_aspect <= 0.0) { - return; - } - - rex::cvar::SetFlagByName("skate3_ultrawide_target_aspect", std::to_string(target_aspect)); - - display_height = - uint32_t(std::clamp(rex::cvar::Query("skate3_ultrawide_base_height"), 480, 2160)); - display_width = - uint32_t(std::clamp(target_aspect * double(display_height) + 0.5, 1.0, 4095.0)); -} - -float ComputeSkate3UltrawideHorPlusScale() { - const double manual_scale = rex::cvar::Query("skate3_ultrawide_hor_plus_scale"); - if (manual_scale > 0.0) { - return float(std::clamp(manual_scale, 0.25, 4.0)); - } - - constexpr double kBaseAspect = 16.0 / 9.0; - double aspect = GetSkate3UltrawideTargetAspect(); - if (aspect <= 0.0) { - const int32_t display_width = REXCVAR_GET(video_mode_width); - const int32_t display_height = REXCVAR_GET(video_mode_height); - if (display_width <= 0 || display_height <= 0) { - return 1.0f; - } - aspect = double(display_width) / double(display_height); - } - - if (aspect <= kBaseAspect + 0.01f) { - return 1.0f; - } - - return float(std::clamp(kBaseAspect / aspect, 0.25, 1.0)); -} - -Skate3UltrawideDrawState QuerySkate3UltrawideDrawState() { - Skate3UltrawideDrawState state; - state.correction_active = rex::cvar::Query("skate3_ultrawide") && - rex::cvar::Query("skate3_ultrawide_hor_plus") && - ultrawide_debug::IsSkate3GameplayUltrawideActive() && - !rex::cvar::Query("skate3_ultrawide_disable_ndc_correction"); - if (!state.correction_active) { - return state; - } - - state.hor_plus_scale = ComputeSkate3UltrawideHorPlusScale(); - state.skip_shadow_targets = rex::cvar::Query("skate3_ultrawide_skip_shadow_targets"); - state.skip_screen_space_targets = - rex::cvar::Query("skate3_ultrawide_skip_screen_space_targets"); - state.shadow_skip_mode = uint32_t(std::clamp( - rex::cvar::Query("skate3_ultrawide_shadow_skip_mode"), 1, 3)); - return state; -} - -Skate3UltrawideDrawState SnapshotSkate3UltrawideDrawState(uint64_t frame) { - thread_local uint64_t cached_frame = 0; - thread_local Skate3UltrawideDrawState cached_state; - if (cached_frame != frame) { - cached_state = QuerySkate3UltrawideDrawState(); - cached_frame = frame; - } - return cached_state; -} - -// Frame-cached read of the cross-TU draw trace flag - the registry Query is a -// mutex + string hash, too expensive to run for every draw. -bool QuerySkate3UltrawideTraceDraws(uint64_t frame) { - thread_local uint64_t cached_frame = UINT64_MAX; - thread_local bool cached_value = false; - if (cached_frame != frame) { - cached_value = rex::cvar::Query("skate3_ultrawide_trace_draws"); - cached_frame = frame; - } - return cached_value; -} - -bool IsSkate3UltrawideDisplayViewport(const draw_util::ViewportInfo& viewport_info) { - const uint32_t width = viewport_info.xy_extent[0]; - const uint32_t height = viewport_info.xy_extent[1]; - if (!width || !height) { - return false; - } - - const uint64_t scaled_width = uint64_t(width) * 9; - const uint64_t scaled_height = uint64_t(height) * 16; - const uint64_t difference = - scaled_width > scaled_height ? scaled_width - scaled_height : scaled_height - scaled_width; - return difference <= scaled_height / 20; -} - -bool ShouldApplySkate3UltrawideHorPlus( - const ultrawide_debug::TargetKey& target_key, bool primitive_polygonal, - Shader::HostVertexShaderType host_vertex_shader_type, reg::PA_CL_VTE_CNTL pa_cl_vte_cntl, - const draw_util::ViewportInfo& viewport_info, - reg::RB_DEPTHCONTROL normalized_depth_control, - const Skate3UltrawideDrawState& ultrawide_state) { - if (!ultrawide_state.correction_active) { - return false; - } - - // The base classifier matches the Direct3D 12 build exactly. Pre-divided XY - // (screen-space/orthographic passes), non-kVertex host shaders, and depth - // passes without a depth write can leak widening into the side areas as - // menu/HUD fragments; excluding them is gated behind - // skate3_ultrawide_skip_screen_space_targets (off by default) so the Vulkan - // default matches the D3D12-tuned target list unless opted in. - const bool screen_space_pass = - host_vertex_shader_type != Shader::HostVertexShaderType::kVertex || - !normalized_depth_control.z_write_enable || pa_cl_vte_cntl.vtx_xy_fmt; - const bool default_enabled = - primitive_polygonal && normalized_depth_control.z_enable && - IsSkate3UltrawideDisplayViewport(viewport_info) && - !(ultrawide_state.skip_screen_space_targets && screen_space_pass); - const bool selected = ultrawide_debug::ShouldApplyAndRecord(target_key, default_enabled); - if (selected && ultrawide_state.skip_shadow_targets && - ultrawide_debug::IsShadowMapCandidate(target_key, ultrawide_state.shadow_skip_mode)) { - return false; - } - return selected; -} - bool IsInGameContextActive(const system::KernelState* kernel_state) { if (!kernel_state) { return false; @@ -2954,13 +2790,17 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontb kernel::xboxkrnl::VdQueryVideoMode(&video_mode); uint32_t display_width = std::max(uint32_t(1), uint32_t(video_mode.display_width)); uint32_t display_height = std::max(uint32_t(1), uint32_t(video_mode.display_height)); - ApplySkate3UltrawidePresenterAspect(presenter, display_width, display_height); + // Ultrawide: while the native renderer is serving frames, allocate the + // guest output at the wide display aspect; emulated frames keep the + // frontbuffer aspect and present pillarboxed. + const bool native_wide_output = ApplyNativeGuestOutputWideAspect( + guest_output_width, guest_output_height, display_width, display_height); presenter->RefreshGuestOutput( guest_output_width, guest_output_height, display_width, display_height, [this, guest_output_width, guest_output_height, display_width, display_height, - frontbuffer_format, swap_texture_view, swap_post_effect, - swap_source_needs_rb_swap](ui::Presenter::GuestOutputRefreshContext& context) -> bool { + frontbuffer_format, swap_texture_view, swap_post_effect, swap_source_needs_rb_swap, + native_wide_output](ui::Presenter::GuestOutputRefreshContext& context) -> bool { // In case the swap command is the only one in the frame. if (!BeginSubmission(true)) { return false; @@ -3028,6 +2868,12 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontb // the gamma pass below records into the same deferred command // buffer. NativeRhiEndFrame(native_rhi_device_); + if (native_wide_output) { + // The output was sized wide for the native renderer; the + // emulated blit writes frontbuffer-aspect content, so keep the + // previously presented image and resize on the next refresh. + return false; + } } bool swap_source_requires_compute_rb_swap = @@ -5086,69 +4932,6 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type, uint32_t } } } - bool skate3_ultrawide_submit_draw = true; - // Frame-cached: the registry Query takes the registry mutex and hashes the - // name, far too expensive for a per-draw debug-flag check. - if (QuerySkate3UltrawideTraceDraws(frame_current_)) { - ultrawide_debug::DrawFingerprint fingerprint; - fingerprint.bucket = - memexport_writes_possible - ? ultrawide_debug::DrawBucket::kMemexport - : (edram_mode == xenos::EdramMode::kDepthOnly - ? ultrawide_debug::DrawBucket::kDepthOnly - : (pixel_shader == nullptr ? ultrawide_debug::DrawBucket::kNoPixelShader - : ultrawide_debug::DrawBucket::kMainColorDepth)); - fingerprint.vertex_shader_hash = vertex_shader->ucode_data_hash(); - fingerprint.pixel_shader_hash = pixel_shader ? pixel_shader->ucode_data_hash() : 0; - fingerprint.primitive_type = uint32_t(primitive_processing_result.host_primitive_type); - fingerprint.vertex_count = host_draw_vertex_count; - fingerprint.primitive_count = host_draw_primitive_count; - fingerprint.packet_ptr = current_packet_ptr_; - if (index_buffer_info) { - fingerprint.index_guest_base = index_buffer_info->guest_base; - fingerprint.index_length = uint32_t(index_buffer_info->length); - } - const Shader::ConstantRegisterMap& constant_map = vertex_shader->constant_register_map(); - size_t fetch_count = 0; - for (uint32_t i = 0; i < rex::countof(constant_map.vertex_fetch_bitmap); ++i) { - uint32_t fetch_bits = constant_map.vertex_fetch_bitmap[i]; - uint32_t bit = 0; - while (fetch_count < ultrawide_debug::DrawFingerprint::kVertexFetchCount && - rex::bit_scan_forward(fetch_bits, &bit)) { - fetch_bits &= ~(uint32_t(1) << bit); - xenos::xe_gpu_vertex_fetch_t fetch = regs.GetVertexFetch(i * 32 + bit); - fingerprint.vertex_fetch_address[fetch_count] = fetch.address << 2; - fingerprint.vertex_fetch_size[fetch_count] = fetch.size << 2; - ++fetch_count; - } - if (fetch_count >= ultrawide_debug::DrawFingerprint::kVertexFetchCount) { - break; - } - } - size_t texture_count = 0; - uint32_t texture_bits = used_texture_mask; - uint32_t texture_index = 0; - while (texture_count < ultrawide_debug::DrawFingerprint::kTextureFetchCount && - rex::bit_scan_forward(texture_bits, &texture_index)) { - texture_bits &= ~(UINT32_C(1) << texture_index); - TextureCache::DebugActiveTextureBinding binding; - if (!texture_cache_->DebugGetActiveTextureBinding(texture_index, binding)) { - continue; - } - fingerprint.texture_key_hash[texture_count] = binding.key_hash; - fingerprint.texture_fetch_index[texture_count] = binding.fetch_index; - fingerprint.texture_base_address[texture_count] = binding.base_address; - fingerprint.texture_base_length[texture_count] = binding.base_length; - fingerprint.texture_width[texture_count] = binding.width; - fingerprint.texture_height[texture_count] = binding.height; - fingerprint.texture_format[texture_count] = binding.format; - ++texture_count; - } - skate3_ultrawide_submit_draw = ultrawide_debug::RecordDrawFingerprint(fingerprint); - } - if (!skate3_ultrawide_submit_draw) { - return true; - } pre_draw_stage_timer.Stop(); // Draw. @@ -8238,39 +8021,10 @@ void VulkanCommandProcessor::UpdateSystemConstantValues( system_constants_.tessellation_factor_range_max = tessellation_factor_max; // Conversion to host normalized device coordinates. - ultrawide_debug::TargetKey skate3_ultrawide_target_key; - for (uint32_t i = 0; i < 4; ++i) { - skate3_ultrawide_target_key.color_info[i] = color_infos[i].value; - } - skate3_ultrawide_target_key.depth_info = rb_depth_info.value; - skate3_ultrawide_target_key.surface_info = rb_surface_info.value; - skate3_ultrawide_target_key.viewport_x = viewport_info.xy_offset[0]; - skate3_ultrawide_target_key.viewport_y = viewport_info.xy_offset[1]; - skate3_ultrawide_target_key.viewport_width = viewport_info.xy_extent[0]; - skate3_ultrawide_target_key.viewport_height = viewport_info.xy_extent[1]; - skate3_ultrawide_target_key.color_mask = normalized_color_mask; - skate3_ultrawide_target_key.depth_control = normalized_depth_control.value; - skate3_ultrawide_target_key.pa_cl_vte_cntl = pa_cl_vte_cntl.value; - skate3_ultrawide_target_key.primitive_type = uint32_t(vgt_draw_initiator.prim_type); - skate3_ultrawide_target_key.host_vertex_shader_type = - uint32_t(primitive_processing_result.host_vertex_shader_type); - skate3_ultrawide_target_key.vertex_shader_hash = vertex_shader_hash; - skate3_ultrawide_target_key.pixel_shader_hash = pixel_shader_hash; - const Skate3UltrawideDrawState skate3_ultrawide_state = - SnapshotSkate3UltrawideDrawState(frame_current_); - const bool skate3_ultrawide_hor_plus_draw = ShouldApplySkate3UltrawideHorPlus( - skate3_ultrawide_target_key, primitive_polygonal, - primitive_processing_result.host_vertex_shader_type, pa_cl_vte_cntl, viewport_info, - normalized_depth_control, skate3_ultrawide_state); - const float skate3_ultrawide_hor_plus_scale = - skate3_ultrawide_hor_plus_draw ? skate3_ultrawide_state.hor_plus_scale : 1.0f; for (uint32_t i = 0; i < 3; ++i) { - const float ndc_scale = - i == 0 ? viewport_info.ndc_scale[i] * skate3_ultrawide_hor_plus_scale - : viewport_info.ndc_scale[i]; - dirty |= system_constants_.ndc_scale[i] != ndc_scale; + dirty |= system_constants_.ndc_scale[i] != viewport_info.ndc_scale[i]; dirty |= system_constants_.ndc_offset[i] != viewport_info.ndc_offset[i]; - system_constants_.ndc_scale[i] = ndc_scale; + system_constants_.ndc_scale[i] = viewport_info.ndc_scale[i]; system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i]; } diff --git a/src/kernel/xam/apps/xgi_app.cpp b/src/kernel/xam/apps/xgi_app.cpp index 2bde125..bfd3446 100644 --- a/src/kernel/xam/apps/xgi_app.cpp +++ b/src/kernel/xam/apps/xgi_app.cpp @@ -14,8 +14,8 @@ #include #include -#include #include +#include #include #include #include @@ -49,8 +49,7 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t context_value = memory::load_and_swap(buffer + 20); auto old_value = kernel_state_->GetUserContext(user_index, context_id); kernel_state_->SetUserContext(user_index, context_id, context_value); - rex::graphics::ultrawide_debug::NotifySkate3GameplayContext(user_index, context_id, - context_value); + rex::kernel::guest_presence::NotifyContext(user_index, context_id, context_value); if (rex::cvar::Query("skate3_demo_path") && user_index == 0 && context_id == 0x8001) { // Boot automation is strictly ONE-SHOT: the dialog A auto-tap exists diff --git a/src/kernel/xboxkrnl/xboxkrnl_video.cpp b/src/kernel/xboxkrnl/xboxkrnl_video.cpp index ab32f58..73f5c99 100644 --- a/src/kernel/xboxkrnl/xboxkrnl_video.cpp +++ b/src/kernel/xboxkrnl/xboxkrnl_video.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -41,24 +40,18 @@ constexpr uint32_t kDisplayGammaType = 2; constexpr double kDisplayGammaPower = 2.22222233; constexpr double kSkate3BaseAspect = 16.0 / 9.0; -bool Skate3UltrawidePresentationActive() { +// Ultrawide keeps the guest on a standard 16:9 video mode: the widening is +// done natively (wide guest output + Hor+ projection), never by handing the +// game a wide mode. +bool Skate3UltrawideConfigured() { return rex::cvar::Query("skate3_ultrawide") && - rex::graphics::ultrawide_debug::IsSkate3GameplayUltrawideActive() && - rex::cvar::Query("skate3_ultrawide_target_aspect") > - kSkate3BaseAspect + 0.01; -} - -bool Skate3UltrawideConfiguredBeforeGameplay() { - return rex::cvar::Query("skate3_ultrawide") && - !rex::graphics::ultrawide_debug::IsSkate3GameplayUltrawideActive() && rex::cvar::Query("skate3_ultrawide_target_aspect") > kSkate3BaseAspect + 0.01; } uint32_t GetConfiguredVideoModeWidth() { int32_t configured_width = REXCVAR_GET(video_mode_width); - if (!rex::cvar::HasNonDefaultValue("video_mode_width") && - !Skate3UltrawidePresentationActive() && !Skate3UltrawideConfiguredBeforeGameplay()) { + if (!rex::cvar::HasNonDefaultValue("video_mode_width") && !Skate3UltrawideConfigured()) { if (rex::cvar::HasNonDefaultValue("window_width") && REXCVAR_GET(window_width) > 0) { configured_width = REXCVAR_GET(window_width); } else { @@ -75,8 +68,7 @@ uint32_t GetConfiguredVideoModeWidth() { uint32_t GetConfiguredVideoModeHeight() { int32_t configured_height = REXCVAR_GET(video_mode_height); - if (!rex::cvar::HasNonDefaultValue("video_mode_height") && - !Skate3UltrawidePresentationActive() && !Skate3UltrawideConfiguredBeforeGameplay()) { + if (!rex::cvar::HasNonDefaultValue("video_mode_height") && !Skate3UltrawideConfigured()) { if (rex::cvar::HasNonDefaultValue("window_height") && REXCVAR_GET(window_height) > 0) { configured_height = REXCVAR_GET(window_height); } else { diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index fa45cd3..8b4b31d 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -28,7 +28,6 @@ set(REXUI_OVERLAY_SOURCES overlay/wizard_screen.cpp overlay/simple_settings_overlay.cpp overlay/settings_overlay.cpp - overlay/ultrawide_targets_overlay.cpp ) # Vulkan UI backend diff --git a/src/ui/overlay/simple_settings_overlay.cpp b/src/ui/overlay/simple_settings_overlay.cpp index 254220d..f2c93b4 100644 --- a/src/ui/overlay/simple_settings_overlay.cpp +++ b/src/ui/overlay/simple_settings_overlay.cpp @@ -1364,7 +1364,9 @@ void SimpleSettingsDialog::BuildRows(std::vector& rows, int category) { RowSpec row; row.kind = RowSpec::kEnum; row.label = "Aspect Ratio"; - row.desc = "21:9 widens the world rendering for ultrawide displays."; + row.desc = + "21:9 widens the world rendering for ultrawide displays. Requires " + "the native renderer; the emulated renderer presents 16:9."; row.value_note = "21:9 is experimental"; for (const char* label : kAspectRatioLabels) { row.options.push_back(label); diff --git a/src/ui/overlay/ultrawide_targets_overlay.cpp b/src/ui/overlay/ultrawide_targets_overlay.cpp deleted file mode 100644 index 174ca4e..0000000 --- a/src/ui/overlay/ultrawide_targets_overlay.cpp +++ /dev/null @@ -1,1431 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace rex::ui { - -namespace { - -std::string Hex(uint64_t value, uint32_t width) { - std::ostringstream stream; - stream << std::uppercase << std::hex << std::setw(width) << std::setfill('0') << value; - return stream.str(); -} - -std::string TargetGroupLabel(const graphics::ultrawide_debug::TargetEntry& entry) { - const auto& key = entry.key; - std::ostringstream stream; - stream << "RT0 " << Hex(key.color_info[0], 8) << " / D " << Hex(key.depth_info, 8) - << " / " << key.viewport_width << "x" << key.viewport_height; - return stream.str(); -} - -const char* ScreenCallbackKindLabel(graphics::ultrawide_debug::ScreenCallbackKind kind) { - switch (kind) { - case graphics::ultrawide_debug::ScreenCallbackKind::kWidth: - return "Width"; - case graphics::ultrawide_debug::ScreenCallbackKind::kHeight: - return "Height"; - case graphics::ultrawide_debug::ScreenCallbackKind::kScreenFlag: - return "Screen flag"; - case graphics::ultrawide_debug::ScreenCallbackKind::kWide: - return "Wide"; - } - return "Unknown"; -} - -const char* DrawBucketLabel(graphics::ultrawide_debug::DrawBucket bucket) { - switch (bucket) { - case graphics::ultrawide_debug::DrawBucket::kMainColorDepth: - return "Main"; - case graphics::ultrawide_debug::DrawBucket::kDepthOnly: - return "Depth"; - case graphics::ultrawide_debug::DrawBucket::kCopyResolve: - return "Copy"; - case graphics::ultrawide_debug::DrawBucket::kMemexport: - return "Memexport"; - case graphics::ultrawide_debug::DrawBucket::kNoPixelShader: - return "No PS"; - } - return "Unknown"; -} - -struct DrawDiffEntry { - graphics::ultrawide_debug::DrawFingerprint fingerprint; - uint64_t visible_count = 0; - uint64_t popped_count = 0; -}; - -enum class DrawGroupMode : uint32_t { - kShader = 0, - kObject = 1, - kTexture = 2, -}; - -struct DrawGroupKey { - DrawGroupMode mode = DrawGroupMode::kObject; - graphics::ultrawide_debug::DrawBucket bucket = - graphics::ultrawide_debug::DrawBucket::kMainColorDepth; - uint32_t guest_render_object = 0; - uint32_t guest_render_vtable = 0; - uint32_t guest_render_target = 0; - uint64_t vertex_shader_hash = 0; - uint64_t pixel_shader_hash = 0; - uint32_t primitive_type = 0; - uint32_t index_guest_base = 0; - uint32_t index_length = 0; - std::array - vertex_fetch_address{}; - std::array - vertex_fetch_size{}; - std::array - texture_key_hash{}; - - bool operator==(const DrawGroupKey& other) const { - return mode == other.mode && bucket == other.bucket && - guest_render_object == other.guest_render_object && - guest_render_vtable == other.guest_render_vtable && - guest_render_target == other.guest_render_target && - vertex_shader_hash == other.vertex_shader_hash && - pixel_shader_hash == other.pixel_shader_hash && primitive_type == other.primitive_type && - index_guest_base == other.index_guest_base && index_length == other.index_length && - vertex_fetch_address == other.vertex_fetch_address && - vertex_fetch_size == other.vertex_fetch_size && - texture_key_hash == other.texture_key_hash; - } -}; - -struct DrawGroupKeyHash { - size_t operator()(const DrawGroupKey& key) const { - uint64_t hash = 1469598103934665603ull; - auto hash_add = [&hash](uint64_t value) { - for (size_t i = 0; i < sizeof(value); ++i) { - hash ^= uint8_t(value >> (i * 8)); - hash *= 1099511628211ull; - } - }; - hash_add(uint32_t(key.mode)); - hash_add(uint32_t(key.bucket)); - hash_add(key.guest_render_object); - hash_add(key.guest_render_vtable); - hash_add(key.guest_render_target); - hash_add(key.vertex_shader_hash); - hash_add(key.pixel_shader_hash); - hash_add(key.primitive_type); - hash_add(key.index_guest_base); - hash_add(key.index_length); - for (uint32_t fetch_address : key.vertex_fetch_address) { - hash_add(fetch_address); - } - for (uint32_t fetch_size : key.vertex_fetch_size) { - hash_add(fetch_size); - } - for (uint64_t texture_hash : key.texture_key_hash) { - hash_add(texture_hash); - } - return size_t(hash); - } -}; - -struct DrawGroupEntry { - DrawGroupKey key; - graphics::ultrawide_debug::DrawFingerprint representative; - graphics::ultrawide_debug::DrawFingerprint textured_representative; - graphics::ultrawide_debug::DrawFingerprint main_textured_representative; - uint64_t group_hash = 0; - uint64_t draw_count = 0; - uint64_t textured_draw_count = 0; - uint64_t main_textured_draw_count = 0; - uint64_t submitted_count = 0; - uint64_t skipped_count = 0; - uint64_t vertices = 0; - uint64_t primitives = 0; - uint32_t min_vertex_count = 0; - uint32_t max_vertex_count = 0; - uint32_t min_primitive_count = 0; - uint32_t max_primitive_count = 0; - uint32_t exact_count = 0; - uint32_t enabled_count = 0; - uint64_t last_seen_order = 0; - uint64_t last_textured_seen_order = 0; - uint64_t last_main_textured_seen_order = 0; - uint64_t last_seen_ms = 0; - uint64_t last_textured_seen_ms = 0; - uint64_t last_main_textured_seen_ms = 0; - std::vector exact_hashes; -}; - -struct DrawGroupDiffEntry { - DrawGroupKey key; - graphics::ultrawide_debug::DrawFingerprint representative; - uint64_t group_hash = 0; - uint64_t visible_count = 0; - uint64_t popped_count = 0; -}; - -struct DrawWatchEvent { - uint64_t sequence = 0; - bool present = false; - uint64_t age = 0; - uint32_t packet_ptr = 0; - uint32_t index_guest_base = 0; - uint32_t vertex_fetch_address = 0; - uint64_t group_hash = 0; -}; - -std::vector g_visible_draw_capture; -std::vector g_popped_draw_capture; -std::optional g_watched_draw_group; -uint64_t g_watched_draw_group_hash = 0; -std::deque g_draw_watch_events; -uint64_t g_draw_watch_event_sequence = 0; -bool g_draw_watch_state_valid = false; -bool g_draw_watch_present = false; -int g_draw_watch_age_threshold = 120; - -uint64_t NowMilliseconds() { - return uint64_t(std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count()); -} - -bool DrawHasTexture(const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - for (uint64_t texture_hash : fingerprint.texture_key_hash) { - if (texture_hash != 0) { - return true; - } - } - return false; -} - -DrawGroupKey MakeDrawGroupKey(const graphics::ultrawide_debug::DrawFingerprint& fingerprint, - DrawGroupMode mode) { - DrawGroupKey key; - key.mode = mode; - key.guest_render_object = fingerprint.guest_render_object; - key.guest_render_vtable = fingerprint.guest_render_vtable; - key.guest_render_target = fingerprint.guest_render_target; - - if (mode == DrawGroupMode::kShader) { - key.bucket = fingerprint.bucket; - key.vertex_shader_hash = fingerprint.vertex_shader_hash; - key.pixel_shader_hash = fingerprint.pixel_shader_hash; - key.primitive_type = fingerprint.primitive_type; - std::copy(std::begin(fingerprint.vertex_fetch_size), std::end(fingerprint.vertex_fetch_size), - key.vertex_fetch_size.begin()); - return key; - } - - if (mode == DrawGroupMode::kTexture) { - size_t texture_count = 0; - for (uint64_t texture_hash : fingerprint.texture_key_hash) { - if (texture_hash != 0 && texture_count < key.texture_key_hash.size()) { - key.texture_key_hash[texture_count++] = texture_hash; - } - } - std::sort(key.texture_key_hash.begin(), key.texture_key_hash.end()); - if (texture_count != 0) { - return key; - } - } - - if (mode == DrawGroupMode::kObject && fingerprint.guest_render_object != 0) { - key.bucket = fingerprint.bucket; - return key; - } - - key.index_guest_base = fingerprint.index_guest_base; - key.index_length = fingerprint.index_length; - std::copy(std::begin(fingerprint.vertex_fetch_address), - std::end(fingerprint.vertex_fetch_address), key.vertex_fetch_address.begin()); - std::copy(std::begin(fingerprint.vertex_fetch_size), std::end(fingerprint.vertex_fetch_size), - key.vertex_fetch_size.begin()); - return key; -} - -uint64_t HashDrawGroupKey(const DrawGroupKey& key) { - return uint64_t(DrawGroupKeyHash{}(key)); -} - -std::string DrawObjectLabel(const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - std::ostringstream stream; - if (fingerprint.guest_render_object != 0) { - stream << "Obj " << Hex(fingerprint.guest_render_object, 8); - if (fingerprint.guest_render_vtable != 0) { - stream << " VT " << Hex(fingerprint.guest_render_vtable, 8); - } - return stream.str(); - } - if (fingerprint.index_guest_base != 0) { - stream << "IB " << Hex(fingerprint.index_guest_base, 8); - if (fingerprint.index_length != 0) { - stream << "+" << std::uppercase << std::hex << fingerprint.index_length; - } - } else { - stream << "NoIB"; - } - for (size_t i = 0; i < graphics::ultrawide_debug::DrawFingerprint::kVertexFetchCount; ++i) { - if (fingerprint.vertex_fetch_address[i] == 0) { - continue; - } - stream << " VF" << i << " " << Hex(fingerprint.vertex_fetch_address[i], 8); - break; - } - return stream.str(); -} - -std::string DrawTextureLabel(const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - for (size_t i = 0; i < graphics::ultrawide_debug::DrawFingerprint::kTextureFetchCount; ++i) { - if (fingerprint.texture_key_hash[i] == 0) { - continue; - } - std::ostringstream stream; - stream << "t" << fingerprint.texture_fetch_index[i] << " " - << Hex(fingerprint.texture_key_hash[i], 16); - if (fingerprint.texture_width[i] != 0 || fingerprint.texture_height[i] != 0) { - stream << " " << fingerprint.texture_width[i] << "x" << fingerprint.texture_height[i]; - } - if (fingerprint.texture_base_address[i] != 0) { - stream << " @" << Hex(fingerprint.texture_base_address[i], 8); - } - return stream.str(); - } - return ""; -} - -uint32_t FirstVertexFetchAddress( - const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - for (uint32_t address : fingerprint.vertex_fetch_address) { - if (address != 0) { - return address; - } - } - return 0; -} - -uint32_t FirstTextureBaseAddress( - const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - for (uint32_t address : fingerprint.texture_base_address) { - if (address != 0) { - return address; - } - } - return 0; -} - -const graphics::ultrawide_debug::DrawFingerprint& BestWatchFingerprint( - const DrawGroupEntry* group, - const graphics::ultrawide_debug::DrawFingerprint& fallback) { - if (group && group->last_main_textured_seen_order != 0) { - return group->main_textured_representative; - } - if (group && group->last_textured_seen_order != 0) { - return group->textured_representative; - } - return fallback; -} - -void SetWatchedDrawGroup(const DrawGroupKey& key, uint64_t group_hash, - const graphics::ultrawide_debug::DrawFingerprint& fingerprint) { - g_watched_draw_group = key; - g_watched_draw_group_hash = group_hash; - g_draw_watch_events.clear(); - g_draw_watch_state_valid = false; - graphics::ultrawide_debug::SetGuestStoreWatchTargets( - fingerprint.index_guest_base, FirstVertexFetchAddress(fingerprint), - FirstTextureBaseAddress(fingerprint), group_hash); -} - -std::vector BuildDrawGroups( - const std::vector& entries, - DrawGroupMode mode) { - std::unordered_map rows; - for (const auto& entry : entries) { - DrawGroupKey key = MakeDrawGroupKey(entry.fingerprint, mode); - auto [it, inserted] = rows.try_emplace(key); - DrawGroupEntry& row = it->second; - if (inserted) { - row.key = key; - row.representative = entry.fingerprint; - row.group_hash = HashDrawGroupKey(key); - row.min_vertex_count = entry.fingerprint.vertex_count; - row.max_vertex_count = entry.fingerprint.vertex_count; - row.min_primitive_count = entry.fingerprint.primitive_count; - row.max_primitive_count = entry.fingerprint.primitive_count; - } else { - row.min_vertex_count = std::min(row.min_vertex_count, entry.fingerprint.vertex_count); - row.max_vertex_count = std::max(row.max_vertex_count, entry.fingerprint.vertex_count); - row.min_primitive_count = std::min(row.min_primitive_count, entry.fingerprint.primitive_count); - row.max_primitive_count = std::max(row.max_primitive_count, entry.fingerprint.primitive_count); - } - if (entry.last_seen_order >= row.last_seen_order) { - row.representative = entry.fingerprint; - row.last_seen_ms = entry.last_seen_ms; - } - const bool has_texture = DrawHasTexture(entry.fingerprint); - const bool main_textured = - has_texture && - entry.fingerprint.bucket == graphics::ultrawide_debug::DrawBucket::kMainColorDepth; - if (has_texture) { - row.textured_draw_count += entry.draw_count; - if (entry.last_seen_order >= row.last_textured_seen_order) { - row.textured_representative = entry.fingerprint; - row.last_textured_seen_order = entry.last_seen_order; - row.last_textured_seen_ms = entry.last_seen_ms; - } - } - if (main_textured) { - row.main_textured_draw_count += entry.draw_count; - if (entry.last_seen_order >= row.last_main_textured_seen_order) { - row.main_textured_representative = entry.fingerprint; - row.last_main_textured_seen_order = entry.last_seen_order; - row.last_main_textured_seen_ms = entry.last_seen_ms; - } - } - row.draw_count += entry.draw_count; - row.submitted_count += entry.submitted_count; - row.skipped_count += entry.skipped_count; - row.vertices += entry.vertices; - row.primitives += entry.primitives; - ++row.exact_count; - if (entry.enabled) { - ++row.enabled_count; - } - row.last_seen_order = std::max(row.last_seen_order, entry.last_seen_order); - row.last_seen_ms = std::max(row.last_seen_ms, entry.last_seen_ms); - row.exact_hashes.push_back(entry.hash ? entry.hash - : graphics::ultrawide_debug::HashDrawFingerprint(entry.fingerprint)); - } - - std::vector groups; - groups.reserve(rows.size()); - for (auto& [_, row] : rows) { - groups.push_back(std::move(row)); - } - std::sort(groups.begin(), groups.end(), [](const DrawGroupEntry& a, const DrawGroupEntry& b) { - if (a.key.bucket != b.key.bucket) { - return uint32_t(a.key.bucket) < uint32_t(b.key.bucket); - } - if (a.draw_count != b.draw_count) { - return a.draw_count > b.draw_count; - } - if (a.exact_count != b.exact_count) { - return a.exact_count > b.exact_count; - } - return a.group_hash < b.group_hash; - }); - return groups; -} - -std::vector BuildDrawDiff() { - std::unordered_map rows; - for (const auto& entry : g_visible_draw_capture) { - auto& row = rows[graphics::ultrawide_debug::HashDrawFingerprint(entry.fingerprint)]; - row.fingerprint = entry.fingerprint; - row.visible_count += entry.draw_count; - } - for (const auto& entry : g_popped_draw_capture) { - auto& row = rows[graphics::ultrawide_debug::HashDrawFingerprint(entry.fingerprint)]; - if (!row.visible_count) { - row.fingerprint = entry.fingerprint; - } - row.popped_count += entry.draw_count; - } - - std::vector diff; - for (const auto& [hash, row] : rows) { - (void)hash; - if (row.visible_count > row.popped_count) { - diff.push_back(row); - } - } - std::sort(diff.begin(), diff.end(), [](const DrawDiffEntry& a, const DrawDiffEntry& b) { - const uint64_t a_delta = a.visible_count - a.popped_count; - const uint64_t b_delta = b.visible_count - b.popped_count; - if (a_delta != b_delta) { - return a_delta > b_delta; - } - if (a.fingerprint.vertex_count != b.fingerprint.vertex_count) { - return a.fingerprint.vertex_count > b.fingerprint.vertex_count; - } - return graphics::ultrawide_debug::HashDrawFingerprint(a.fingerprint) < - graphics::ultrawide_debug::HashDrawFingerprint(b.fingerprint); - }); - return diff; -} - -std::vector BuildDrawGroupDiff(DrawGroupMode mode) { - std::unordered_map rows; - for (const auto& entry : g_visible_draw_capture) { - DrawGroupKey key = MakeDrawGroupKey(entry.fingerprint, mode); - auto [it, inserted] = rows.try_emplace(key); - DrawGroupDiffEntry& row = it->second; - if (inserted) { - row.key = key; - row.representative = entry.fingerprint; - row.group_hash = HashDrawGroupKey(key); - } - row.visible_count += entry.draw_count; - } - for (const auto& entry : g_popped_draw_capture) { - DrawGroupKey key = MakeDrawGroupKey(entry.fingerprint, mode); - auto [it, inserted] = rows.try_emplace(key); - DrawGroupDiffEntry& row = it->second; - if (inserted) { - row.key = key; - row.representative = entry.fingerprint; - row.group_hash = HashDrawGroupKey(key); - } - row.popped_count += entry.draw_count; - } - - std::vector diff; - for (const auto& [_, row] : rows) { - if (row.visible_count > row.popped_count) { - diff.push_back(row); - } - } - std::sort(diff.begin(), diff.end(), [](const DrawGroupDiffEntry& a, const DrawGroupDiffEntry& b) { - const uint64_t a_delta = a.visible_count - a.popped_count; - const uint64_t b_delta = b.visible_count - b.popped_count; - if (a_delta != b_delta) { - return a_delta > b_delta; - } - return a.group_hash < b.group_hash; - }); - return diff; -} - -} // namespace - -UltrawideTargetsDialog::UltrawideTargetsDialog(ImGuiDrawer* drawer, - std::filesystem::path export_path, - CloseCallback close_callback) - : ImGuiDialog(drawer), - export_path_(std::move(export_path)), - close_callback_(std::move(close_callback)) { - graphics::ultrawide_debug::SetGuestStoreWatchLogPath( - export_path_.parent_path() / "skate3_ultrawide_watch.log"); - graphics::ultrawide_debug::SetTargetRecordingActive(false); - SetDrawActive(false); -} - -UltrawideTargetsDialog::~UltrawideTargetsDialog() = default; - -void UltrawideTargetsDialog::Show() { - visible_ = true; - graphics::ultrawide_debug::SetTargetRecordingActive(true); - SetDrawActive(true); -} - -void UltrawideTargetsDialog::Toggle() { - if (visible_) { - Hide(); - } else { - Show(); - } -} - -void UltrawideTargetsDialog::Hide() { - if (!visible_) { - return; - } - visible_ = false; - graphics::ultrawide_debug::SetTargetRecordingActive(false); - SetDrawActive(false); - if (close_callback_) { - close_callback_(); - } -} - -void UltrawideTargetsDialog::OnDraw(ImGuiIO& io) { - (void)io; - if (!visible_) { - return; - } - - bool open = visible_; - ImGui::SetNextWindowSize(ImVec2(1160.0f, 620.0f), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Ultrawide Render Targets", &open, ImGuiWindowFlags_NoCollapse)) { - ImGui::End(); - if (!open) { - Hide(); - } - return; - } - - auto targets = graphics::ultrawide_debug::SnapshotTargets(); - auto screen_callbacks = graphics::ultrawide_debug::SnapshotScreenCallbacks(); - auto guest_store_watch_events = graphics::ultrawide_debug::SnapshotGuestStoreWatchEvents(); - std::sort(targets.begin(), targets.end(), - [](const graphics::ultrawide_debug::TargetEntry& a, - const graphics::ultrawide_debug::TargetEntry& b) { - if (a.default_enabled != b.default_enabled) { - return a.default_enabled > b.default_enabled; - } - if (a.key.color_info[0] != b.key.color_info[0]) { - return a.key.color_info[0] < b.key.color_info[0]; - } - if (a.key.depth_info != b.key.depth_info) { - return a.key.depth_info < b.key.depth_info; - } - if (a.key.viewport_width != b.key.viewport_width) { - return a.key.viewport_width > b.key.viewport_width; - } - if (a.key.viewport_height != b.key.viewport_height) { - return a.key.viewport_height > b.key.viewport_height; - } - if (a.key.vertex_shader_hash != b.key.vertex_shader_hash) { - return a.key.vertex_shader_hash < b.key.vertex_shader_hash; - } - return a.hash < b.hash; - }); - std::sort(screen_callbacks.begin(), screen_callbacks.end(), - [](const graphics::ultrawide_debug::ScreenCallbackEntry& a, - const graphics::ultrawide_debug::ScreenCallbackEntry& b) { - if (a.kind != b.kind) { - return uint32_t(a.kind) < uint32_t(b.kind); - } - if (a.caller_lr != b.caller_lr) { - return a.caller_lr < b.caller_lr; - } - return a.hash < b.hash; - }); - if (ImGui::Button("Enable all")) { - graphics::ultrawide_debug::SetAllTargetsEnabled(true); - } - ImGui::SameLine(); - if (ImGui::Button("Disable all")) { - graphics::ultrawide_debug::SetAllTargetsEnabled(false); - } - ImGui::SameLine(); - if (ImGui::Button("Reset defaults")) { - graphics::ultrawide_debug::ResetTargetOverrides(); - } - ImGui::SameLine(); - if (ImGui::Button("Save config")) { - std::error_code ec; - std::filesystem::create_directories(export_path_.parent_path(), ec); - const auto saved = graphics::ultrawide_debug::SaveTargets(export_path_); - if (saved) { - REXLOG_INFO("Saved ultrawide render target config to {}", saved->string()); - } else { - REXLOG_WARN("Failed to save ultrawide render target config to {}", export_path_.string()); - } - } - ImGui::SameLine(); - ImGui::Checkbox("Seen this session", &show_only_seen_); - - bool skip_shadow_targets = rex::cvar::Query("skate3_ultrawide_skip_shadow_targets"); - ImGui::SameLine(); - if (ImGui::Checkbox("Skip shadow maps", &skip_shadow_targets)) { - rex::cvar::SetFlagByName("skate3_ultrawide_skip_shadow_targets", - skip_shadow_targets ? "true" : "false"); - } - int shadow_skip_mode = std::clamp(rex::cvar::Query("skate3_ultrawide_shadow_skip_mode"), - 1, 3) - - 1; - const char* shadow_mode_labels[] = {"Atlas", "Atlas+large", "Depth-only"}; - ImGui::SameLine(); - ImGui::SetNextItemWidth(110.0f); - if (ImGui::Combo("Shadow mode", &shadow_skip_mode, shadow_mode_labels, - IM_ARRAYSIZE(shadow_mode_labels))) { - rex::cvar::SetFlagByName("skate3_ultrawide_shadow_skip_mode", - std::to_string(shadow_skip_mode + 1)); - } - - int apply_mode_index = static_cast(graphics::ultrawide_debug::GetApplyMode()); - const char* apply_mode_labels[] = {"Target list", "Force all", "Force none", "Saved only"}; - ImGui::SameLine(); - ImGui::SetNextItemWidth(130.0f); - if (ImGui::Combo("Mode", &apply_mode_index, apply_mode_labels, - IM_ARRAYSIZE(apply_mode_labels))) { - graphics::ultrawide_debug::SetApplyMode( - static_cast(apply_mode_index)); - } - - bool fake_occlusion_queries = - rex::cvar::Query("skate3_ultrawide_fake_occlusion_queries"); - if (ImGui::Checkbox("Fake visibility queries", &fake_occlusion_queries)) { - rex::cvar::SetFlagByName("skate3_ultrawide_fake_occlusion_queries", - fake_occlusion_queries ? "true" : "false"); - } - ImGui::SameLine(); - int fake_occlusion_sample_count = std::clamp( - rex::cvar::Query("skate3_ultrawide_fake_occlusion_sample_count"), 1, 1000000); - ImGui::SetNextItemWidth(110.0f); - if (ImGui::InputInt("Visibility samples", &fake_occlusion_sample_count, 1000, 10000)) { - fake_occlusion_sample_count = std::clamp(fake_occlusion_sample_count, 1, 1000000); - rex::cvar::SetFlagByName("skate3_ultrawide_fake_occlusion_sample_count", - std::to_string(fake_occlusion_sample_count)); - } - - bool widen_game_frustum = - rex::cvar::Query("skate3_ultrawide_widen_game_frustum"); - if (ImGui::Checkbox("Widen game frustum", &widen_game_frustum)) { - rex::cvar::SetFlagByName("skate3_ultrawide_widen_game_frustum", - widen_game_frustum ? "true" : "false"); - } - ImGui::SameLine(); - bool force_main_visibility_flags = - rex::cvar::Query("skate3_ultrawide_force_main_visibility_flags"); - if (ImGui::Checkbox("Force main visibility", &force_main_visibility_flags)) { - rex::cvar::SetFlagByName("skate3_ultrawide_force_main_visibility_flags", - force_main_visibility_flags ? "true" : "false"); - } - ImGui::SameLine(); - bool disable_ndc_correction = - rex::cvar::Query("skate3_ultrawide_disable_ndc_correction"); - if (ImGui::Checkbox("Disable NDC correction", &disable_ndc_correction)) { - rex::cvar::SetFlagByName("skate3_ultrawide_disable_ndc_correction", - disable_ndc_correction ? "true" : "false"); - } - ImGui::SameLine(); - if (ImGui::Button("Trace 1k")) { - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_remaining", "1000"); - } - ImGui::SameLine(); - if (ImGui::Button("Trace 10k")) { - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_remaining", "10000"); - } - ImGui::SameLine(); - if (ImGui::Button("Stop trace")) { - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_remaining", "0"); - } - ImGui::SameLine(); - int texture_trace_remaining = - std::clamp(rex::cvar::Query("skate3_ultrawide_texture_trace_remaining"), 0, - 1000000); - ImGui::SetNextItemWidth(105.0f); - if (ImGui::InputInt("Trace left", &texture_trace_remaining, 100, 1000)) { - texture_trace_remaining = std::clamp(texture_trace_remaining, 0, 1000000); - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_remaining", - std::to_string(texture_trace_remaining)); - } - - bool texture_trace_bind_keys = - rex::cvar::Query("skate3_ultrawide_texture_trace_bind_keys"); - if (ImGui::Checkbox("Trace binds", &texture_trace_bind_keys)) { - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_bind_keys", - texture_trace_bind_keys ? "true" : "false"); - } - ImGui::SameLine(); - bool texture_trace_scaled_resolve = - rex::cvar::Query("skate3_ultrawide_texture_trace_scaled_resolve"); - if (ImGui::Checkbox("Trace scaled", &texture_trace_scaled_resolve)) { - rex::cvar::SetFlagByName("skate3_ultrawide_texture_trace_scaled_resolve", - texture_trace_scaled_resolve ? "true" : "false"); - } - bool ignore_streamer_texture_invalidations = - rex::cvar::Query("skate3_ultrawide_ignore_streamer_texture_invalidations"); - if (ImGui::Checkbox("Pin streamer textures", &ignore_streamer_texture_invalidations)) { - rex::cvar::SetFlagByName("skate3_ultrawide_ignore_streamer_texture_invalidations", - ignore_streamer_texture_invalidations ? "true" : "false"); - } - auto draw_fingerprints = graphics::ultrawide_debug::SnapshotDrawFingerprints(); - std::sort(draw_fingerprints.begin(), draw_fingerprints.end(), - [](const graphics::ultrawide_debug::DrawFingerprintEntry& a, - const graphics::ultrawide_debug::DrawFingerprintEntry& b) { - if (a.fingerprint.bucket != b.fingerprint.bucket) { - return uint32_t(a.fingerprint.bucket) < uint32_t(b.fingerprint.bucket); - } - if (a.fingerprint.packet_ptr != b.fingerprint.packet_ptr) { - return a.fingerprint.packet_ptr < b.fingerprint.packet_ptr; - } - if (a.fingerprint.index_guest_base != b.fingerprint.index_guest_base) { - return a.fingerprint.index_guest_base < b.fingerprint.index_guest_base; - } - return graphics::ultrawide_debug::HashDrawFingerprint(a.fingerprint) < - graphics::ultrawide_debug::HashDrawFingerprint(b.fingerprint); - }); - draw_group_mode_ = std::clamp(draw_group_mode_, 0, 2); - const auto draw_group_mode = static_cast(draw_group_mode_); - auto draw_groups = BuildDrawGroups(draw_fingerprints, draw_group_mode); - - ImGui::Separator(); - bool trace_draws = rex::cvar::Query("skate3_ultrawide_trace_draws"); - if (ImGui::Checkbox("Trace draws", &trace_draws)) { - rex::cvar::SetFlagByName("skate3_ultrawide_trace_draws", trace_draws ? "true" : "false"); - graphics::ultrawide_debug::ClearDrawFingerprints(); - g_visible_draw_capture.clear(); - g_popped_draw_capture.clear(); - } - bool object_stage_trace_continuous = - rex::cvar::Query("skate3_ultrawide_object_stage_trace_continuous"); - if (ImGui::Checkbox("Object stages", &object_stage_trace_continuous)) { - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_trace_continuous", - object_stage_trace_continuous ? "true" : "false"); - } - ImGui::SameLine(); - if (ImGui::Button("Obj trace 300")) { - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_trace_frames_remaining", "300"); - } - ImGui::SameLine(); - if (ImGui::Button("Mark visible")) { - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_marker", "1"); - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_trace_frames_remaining", "60"); - } - ImGui::SameLine(); - if (ImGui::Button("Mark invisible")) { - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_marker", "2"); - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_trace_frames_remaining", "60"); - } - ImGui::SameLine(); - int object_stage_trace_frames = std::clamp( - rex::cvar::Query("skate3_ultrawide_object_stage_trace_frames_remaining"), 0, - 1000000); - ImGui::SetNextItemWidth(88.0f); - if (ImGui::InputInt("Obj left", &object_stage_trace_frames, 30, 300)) { - object_stage_trace_frames = std::clamp(object_stage_trace_frames, 0, 1000000); - rex::cvar::SetFlagByName("skate3_ultrawide_object_stage_trace_frames_remaining", - std::to_string(object_stage_trace_frames)); - } - ImGui::SameLine(); - ImGui::Text("Draw groups: %zu Exact: %zu", draw_groups.size(), draw_fingerprints.size()); - ImGui::SameLine(); - const char* draw_group_mode_labels[] = {"Shader", "Object", "Texture"}; - ImGui::SetNextItemWidth(92.0f); - if (ImGui::Combo("Group", &draw_group_mode_, draw_group_mode_labels, - IM_ARRAYSIZE(draw_group_mode_labels))) { - draw_group_mode_ = std::clamp(draw_group_mode_, 0, 2); - } - ImGui::SameLine(); - ImGui::Checkbox("Expand draw section", &draw_section_expanded_); - ImGui::SameLine(); - ImGui::BeginDisabled(!draw_section_expanded_); - ImGui::Checkbox("Exact rows", &draw_show_exact_fingerprints_); - ImGui::EndDisabled(); - ImGui::SameLine(); - if (ImGui::Button("Capture visible")) { - g_visible_draw_capture = draw_fingerprints; - graphics::ultrawide_debug::ClearDrawFingerprints(); - } - ImGui::SameLine(); - if (ImGui::Button("Capture popped")) { - g_popped_draw_capture = draw_fingerprints; - } - ImGui::SameLine(); - if (ImGui::Button("Clear draw captures")) { - g_visible_draw_capture.clear(); - g_popped_draw_capture.clear(); - graphics::ultrawide_debug::ClearDrawFingerprints(); - } - ImGui::SameLine(); - if (ImGui::Button("Show all draws")) { - graphics::ultrawide_debug::SetAllDrawFingerprintsEnabled(true); - } - ImGui::SameLine(); - if (ImGui::Button("Hide all draws")) { - graphics::ultrawide_debug::SetAllDrawFingerprintsEnabled(false); - } - ImGui::SameLine(); - if (ImGui::Button("Reset draw visibility")) { - graphics::ultrawide_debug::ResetDrawFingerprintOverrides(); - } - - if (g_watched_draw_group) { - const DrawGroupEntry* watched_group = nullptr; - for (const auto& group : draw_groups) { - if (group.key == *g_watched_draw_group) { - watched_group = &group; - break; - } - } - - ImGui::Text("Watch: %s", Hex(g_watched_draw_group_hash, 16).c_str()); - ImGui::SameLine(); - if (ImGui::Button("Clear watch")) { - g_watched_draw_group.reset(); - g_watched_draw_group_hash = 0; - g_draw_watch_events.clear(); - g_draw_watch_state_valid = false; - graphics::ultrawide_debug::ClearGuestStoreWatchTargets(); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(96.0f); - if (ImGui::InputInt("Ms threshold", &g_draw_watch_age_threshold, 10, 50)) { - g_draw_watch_age_threshold = std::clamp(g_draw_watch_age_threshold, 1, 10000); - g_draw_watch_state_valid = false; - } - ImGui::SameLine(); - if (watched_group) { - const uint64_t now_ms = NowMilliseconds(); - const uint64_t age = watched_group->last_seen_ms == 0 - ? UINT64_MAX - : (now_ms > watched_group->last_seen_ms - ? now_ms - watched_group->last_seen_ms - : 0); - const uint64_t textured_age = - watched_group->last_textured_seen_ms == 0 - ? UINT64_MAX - : (now_ms > watched_group->last_textured_seen_ms - ? now_ms - watched_group->last_textured_seen_ms - : 0); - const uint64_t main_textured_age = - watched_group->last_main_textured_seen_ms == 0 - ? UINT64_MAX - : (now_ms > watched_group->last_main_textured_seen_ms - ? now_ms - watched_group->last_main_textured_seen_ms - : 0); - const auto& watch_fingerprint = - watched_group->last_main_textured_seen_order != 0 - ? watched_group->main_textured_representative - : (watched_group->last_textured_seen_order != 0 - ? watched_group->textured_representative - : watched_group->representative); - const bool watch_present = main_textured_age <= uint64_t(g_draw_watch_age_threshold); - if (!g_draw_watch_state_valid || g_draw_watch_present != watch_present) { - DrawWatchEvent event; - event.sequence = ++g_draw_watch_event_sequence; - event.present = watch_present; - event.age = main_textured_age; - event.packet_ptr = watch_fingerprint.packet_ptr; - event.index_guest_base = watch_fingerprint.index_guest_base; - event.vertex_fetch_address = watch_fingerprint.vertex_fetch_address[0]; - event.group_hash = g_watched_draw_group_hash; - g_draw_watch_events.push_front(event); - while (g_draw_watch_events.size() > 24) { - g_draw_watch_events.pop_back(); - } - graphics::ultrawide_debug::RecordDrawWatchTransition( - event.present, event.age, event.packet_ptr, event.index_guest_base, - event.vertex_fetch_address, event.group_hash); - g_draw_watch_state_valid = true; - g_draw_watch_present = watch_present; - } - ImGui::Text("calls=%llu textured=%llu main_tex=%llu exact=%u shown=%llu hidden=%llu", - static_cast(watched_group->draw_count), - static_cast(watched_group->textured_draw_count), - static_cast(watched_group->main_textured_draw_count), - watched_group->exact_count, - static_cast(watched_group->submitted_count), - static_cast(watched_group->skipped_count)); - ImGui::Text("any_ms=%s tex_ms=%s main_tex_ms=%s pkt=%s", - age == UINT64_MAX - ? "n/a" - : std::to_string(age).c_str(), - textured_age == UINT64_MAX - ? "n/a" - : std::to_string(textured_age).c_str(), - main_textured_age == UINT64_MAX - ? "n/a" - : std::to_string(main_textured_age).c_str(), - Hex(watch_fingerprint.packet_ptr, 8).c_str()); - ImGui::Text("Watch object: %s", DrawObjectLabel(watch_fingerprint).c_str()); - ImGui::SameLine(); - ImGui::Text("texture: %s", DrawTextureLabel(watch_fingerprint).c_str()); - } else { - ImGui::TextUnformatted("not present in current group mode"); - if (g_draw_watch_state_valid && g_draw_watch_present) { - DrawWatchEvent event; - event.sequence = ++g_draw_watch_event_sequence; - event.present = false; - event.age = UINT64_MAX; - event.group_hash = g_watched_draw_group_hash; - g_draw_watch_events.push_front(event); - while (g_draw_watch_events.size() > 24) { - g_draw_watch_events.pop_back(); - } - graphics::ultrawide_debug::RecordDrawWatchTransition( - event.present, event.age, event.packet_ptr, event.index_guest_base, - event.vertex_fetch_address, event.group_hash); - g_draw_watch_present = false; - } - } - if (!g_draw_watch_events.empty() && - ImGui::BeginTable("draw_watch_events", 7, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, 92.0f))) { - ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 42.0f); - ImGui::TableSetupColumn("State", ImGuiTableColumnFlags_WidthFixed, 64.0f); - ImGui::TableSetupColumn("Age", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Pkt", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("IB", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("VF0", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Group", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableHeadersRow(); - for (const DrawWatchEvent& event : g_draw_watch_events) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(event.sequence)); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.present ? "present" : "missing"); - ImGui::TableNextColumn(); - if (event.age == UINT64_MAX) { - ImGui::TextUnformatted("n/a"); - } else { - ImGui::Text("%llu", static_cast(event.age)); - } - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.packet_ptr ? Hex(event.packet_ptr, 8).c_str() : ""); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.index_guest_base ? Hex(event.index_guest_base, 8).c_str() - : ""); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.vertex_fetch_address - ? Hex(event.vertex_fetch_address, 8).c_str() - : ""); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(Hex(event.group_hash, 16).c_str()); - } - ImGui::EndTable(); - } - const auto watch_log_path = export_path_.parent_path() / "skate3_ultrawide_watch.log"; - ImGui::Text("Watch log: %s", watch_log_path.string().c_str()); - if (!guest_store_watch_events.empty()) { - ImGui::Text("Guest stores: %zu", guest_store_watch_events.size()); - ImGui::SameLine(); - if (ImGui::Button("Clear store hits")) { - graphics::ultrawide_debug::ClearGuestStoreWatchEvents(); - guest_store_watch_events.clear(); - } - if (ImGui::BeginTable("guest_store_watch_events", 8, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, 130.0f))) { - ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 42.0f); - ImGui::TableSetupColumn("Target", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("LR", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Function", ImGuiTableColumnFlags_WidthFixed, 170.0f); - ImGui::TableSetupColumn("Stack", ImGuiTableColumnFlags_WidthFixed, 260.0f); - ImGui::TableSetupColumn("x", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableHeadersRow(); - for (auto it = guest_store_watch_events.rbegin(); - it != guest_store_watch_events.rend(); ++it) { - const auto& event = *it; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(event.sequence)); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.target.c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(Hex(event.address, 8).c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(Hex(event.value, 8).c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.caller_lr ? Hex(event.caller_lr, 8).c_str() : ""); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.function.c_str()); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(event.stack.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(event.count)); - } - ImGui::EndTable(); - } - } - } - - const float draw_diff_height = draw_section_expanded_ ? 180.0f : 86.0f; - const float draw_live_height = draw_section_expanded_ ? 260.0f : 120.0f; - const auto draw_diff = BuildDrawDiff(); - const auto draw_group_diff = BuildDrawGroupDiff(draw_group_mode); - ImGui::Text("Visible capture: %zu Popped capture: %zu Missing groups: %zu Exact missing: %zu", - g_visible_draw_capture.size(), g_popped_draw_capture.size(), draw_group_diff.size(), - draw_diff.size()); - if (ImGui::BeginTable("draw_group_diff", 13, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, draw_diff_height))) { - ImGui::TableSetupColumn("Draw", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableSetupColumn("Watch", ImGuiTableColumnFlags_WidthFixed, 52.0f); - ImGui::TableSetupColumn("Delta", ImGuiTableColumnFlags_WidthFixed, 62.0f); - ImGui::TableSetupColumn("Vis", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableSetupColumn("Pop", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableSetupColumn("Bucket", ImGuiTableColumnFlags_WidthFixed, 78.0f); - ImGui::TableSetupColumn("Exact", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthFixed, 190.0f); - ImGui::TableSetupColumn("Texture", ImGuiTableColumnFlags_WidthFixed, 250.0f); - ImGui::TableSetupColumn("VS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("PS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("Prim", ImGuiTableColumnFlags_WidthFixed, 54.0f); - ImGui::TableSetupColumn("Group", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableHeadersRow(); - for (const auto& row : draw_group_diff) { - const auto& fp = row.representative; - const uint64_t group_hash = row.group_hash; - const DrawGroupEntry* live_group = nullptr; - for (const auto& group : draw_groups) { - if (group.group_hash == group_hash) { - live_group = &group; - break; - } - } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool draw_enabled = !live_group || live_group->enabled_count > 0; - std::string draw_id = "##draw_group_diff_" + Hex(group_hash, 16); - if (ImGui::Checkbox(draw_id.c_str(), &draw_enabled)) { - if (live_group) { - for (uint64_t exact_hash : live_group->exact_hashes) { - graphics::ultrawide_debug::SetDrawFingerprintEnabled(exact_hash, draw_enabled); - } - } - } - ImGui::TableNextColumn(); - std::string watch_id = "W##draw_group_diff_watch_" + Hex(group_hash, 16); - if (ImGui::SmallButton(watch_id.c_str())) { - SetWatchedDrawGroup(row.key, group_hash, BestWatchFingerprint(live_group, fp)); - } - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(row.visible_count - row.popped_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(row.visible_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(row.popped_count)); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(DrawBucketLabel(fp.bucket)); - ImGui::TableNextColumn(); - ImGui::Text("%u", live_group ? live_group->exact_count : 0); - ImGui::TableNextColumn(); - const std::string object = DrawObjectLabel(fp); - ImGui::TextUnformatted(object.c_str()); - ImGui::TableNextColumn(); - const auto& texture_fp = - live_group && live_group->last_main_textured_seen_order != 0 - ? live_group->main_textured_representative - : (live_group && live_group->last_textured_seen_order != 0 - ? live_group->textured_representative - : fp); - const std::string texture = DrawTextureLabel(texture_fp); - ImGui::TextUnformatted(texture.c_str()); - ImGui::TableNextColumn(); - const std::string vs = Hex(fp.vertex_shader_hash, 16); - ImGui::TextUnformatted(vs.c_str()); - ImGui::TableNextColumn(); - const std::string ps = Hex(fp.pixel_shader_hash, 16); - ImGui::TextUnformatted(ps.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u", fp.primitive_type); - ImGui::TableNextColumn(); - const std::string hash = Hex(group_hash, 16); - ImGui::TextUnformatted(hash.c_str()); - } - ImGui::EndTable(); - } - - if (ImGui::BeginTable("draw_groups", 16, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, draw_live_height))) { - ImGui::TableSetupColumn("Draw", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableSetupColumn("Watch", ImGuiTableColumnFlags_WidthFixed, 52.0f); - ImGui::TableSetupColumn("Calls", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Shown", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Bucket", ImGuiTableColumnFlags_WidthFixed, 78.0f); - ImGui::TableSetupColumn("Exact", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Verts", ImGuiTableColumnFlags_WidthFixed, 92.0f); - ImGui::TableSetupColumn("Prims", ImGuiTableColumnFlags_WidthFixed, 92.0f); - ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthFixed, 190.0f); - ImGui::TableSetupColumn("Texture", ImGuiTableColumnFlags_WidthFixed, 250.0f); - ImGui::TableSetupColumn("VS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("PS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("Prim", ImGuiTableColumnFlags_WidthFixed, 54.0f); - ImGui::TableSetupColumn("Enabled", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Group", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableHeadersRow(); - for (const auto& group : draw_groups) { - const auto& fp = group.representative; - const uint64_t group_hash = group.group_hash; - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool draw_enabled = group.enabled_count > 0; - std::string draw_id = "##draw_group_live_" + Hex(group_hash, 16); - if (ImGui::Checkbox(draw_id.c_str(), &draw_enabled)) { - for (uint64_t exact_hash : group.exact_hashes) { - graphics::ultrawide_debug::SetDrawFingerprintEnabled(exact_hash, draw_enabled); - } - } - ImGui::TableNextColumn(); - std::string watch_id = "W##draw_group_live_watch_" + Hex(group_hash, 16); - if (ImGui::SmallButton(watch_id.c_str())) { - SetWatchedDrawGroup(group.key, group_hash, BestWatchFingerprint(&group, fp)); - } - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(group.draw_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(group.submitted_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(group.skipped_count)); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(DrawBucketLabel(fp.bucket)); - ImGui::TableNextColumn(); - ImGui::Text("%u", group.exact_count); - ImGui::TableNextColumn(); - if (group.min_vertex_count == group.max_vertex_count) { - ImGui::Text("%u", group.min_vertex_count); - } else { - ImGui::Text("%u-%u", group.min_vertex_count, group.max_vertex_count); - } - ImGui::TableNextColumn(); - if (group.min_primitive_count == group.max_primitive_count) { - ImGui::Text("%u", group.min_primitive_count); - } else { - ImGui::Text("%u-%u", group.min_primitive_count, group.max_primitive_count); - } - ImGui::TableNextColumn(); - const std::string object = DrawObjectLabel(fp); - ImGui::TextUnformatted(object.c_str()); - ImGui::TableNextColumn(); - const auto& texture_fp = group.last_main_textured_seen_order != 0 - ? group.main_textured_representative - : (group.last_textured_seen_order != 0 - ? group.textured_representative - : fp); - const std::string texture = DrawTextureLabel(texture_fp); - ImGui::TextUnformatted(texture.c_str()); - ImGui::TableNextColumn(); - const std::string vs = Hex(fp.vertex_shader_hash, 16); - ImGui::TextUnformatted(vs.c_str()); - ImGui::TableNextColumn(); - const std::string ps = Hex(fp.pixel_shader_hash, 16); - ImGui::TextUnformatted(ps.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u", fp.primitive_type); - ImGui::TableNextColumn(); - ImGui::Text("%u/%u", group.enabled_count, group.exact_count); - ImGui::TableNextColumn(); - const std::string hash = Hex(group_hash, 16); - ImGui::TextUnformatted(hash.c_str()); - } - ImGui::EndTable(); - } - - if (draw_section_expanded_ && draw_show_exact_fingerprints_ && - ImGui::BeginTable("draw_fingerprints", 18, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | - ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, 180.0f))) { - ImGui::TableSetupColumn("Draw", ImGuiTableColumnFlags_WidthFixed, 48.0f); - ImGui::TableSetupColumn("Calls", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Shown", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Bucket", ImGuiTableColumnFlags_WidthFixed, 78.0f); - ImGui::TableSetupColumn("Obj", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("VT", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Target", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Verts", ImGuiTableColumnFlags_WidthFixed, 62.0f); - ImGui::TableSetupColumn("Prims", ImGuiTableColumnFlags_WidthFixed, 62.0f); - ImGui::TableSetupColumn("VS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("PS", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("Pkt", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("IB", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("IB Len", ImGuiTableColumnFlags_WidthFixed, 62.0f); - ImGui::TableSetupColumn("VF0", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("VF1", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Hash", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableHeadersRow(); - for (const auto& entry : draw_fingerprints) { - const auto& fp = entry.fingerprint; - const uint64_t fp_hash = entry.hash ? entry.hash : graphics::ultrawide_debug::HashDrawFingerprint(fp); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool draw_enabled = entry.enabled; - std::string draw_id = "##draw_live_" + Hex(fp_hash, 16); - if (ImGui::Checkbox(draw_id.c_str(), &draw_enabled)) { - graphics::ultrawide_debug::SetDrawFingerprintEnabled(fp_hash, draw_enabled); - } - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(entry.draw_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(entry.submitted_count)); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(entry.skipped_count)); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(DrawBucketLabel(fp.bucket)); - ImGui::TableNextColumn(); - const std::string owner = fp.guest_render_object ? Hex(fp.guest_render_object, 8) : ""; - ImGui::TextUnformatted(owner.c_str()); - ImGui::TableNextColumn(); - const std::string owner_vtable = fp.guest_render_vtable ? Hex(fp.guest_render_vtable, 8) : ""; - ImGui::TextUnformatted(owner_vtable.c_str()); - ImGui::TableNextColumn(); - const std::string owner_target = fp.guest_render_target ? Hex(fp.guest_render_target, 8) : ""; - ImGui::TextUnformatted(owner_target.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u", fp.vertex_count); - ImGui::TableNextColumn(); - ImGui::Text("%u", fp.primitive_count); - ImGui::TableNextColumn(); - const std::string vs = Hex(fp.vertex_shader_hash, 16); - ImGui::TextUnformatted(vs.c_str()); - ImGui::TableNextColumn(); - const std::string ps = Hex(fp.pixel_shader_hash, 16); - ImGui::TextUnformatted(ps.c_str()); - ImGui::TableNextColumn(); - const std::string pkt = fp.packet_ptr ? Hex(fp.packet_ptr, 8) : ""; - ImGui::TextUnformatted(pkt.c_str()); - ImGui::TableNextColumn(); - const std::string ib = fp.index_guest_base ? Hex(fp.index_guest_base, 8) : ""; - ImGui::TextUnformatted(ib.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u", fp.index_length); - ImGui::TableNextColumn(); - const std::string vf0 = fp.vertex_fetch_address[0] ? Hex(fp.vertex_fetch_address[0], 8) : ""; - ImGui::TextUnformatted(vf0.c_str()); - ImGui::TableNextColumn(); - const std::string vf1 = fp.vertex_fetch_address[1] ? Hex(fp.vertex_fetch_address[1], 8) : ""; - ImGui::TextUnformatted(vf1.c_str()); - ImGui::TableNextColumn(); - const std::string hash = Hex(fp_hash, 16); - ImGui::TextUnformatted(hash.c_str()); - } - ImGui::EndTable(); - } - - ImGui::Separator(); - ImGui::Text("Targets: %zu", targets.size()); - ImGui::SameLine(); - ImGui::Text("Screen read sites: %zu", screen_callbacks.size()); - ImGui::SameLine(); - ImGui::Text("Target aspect: %.4f", - rex::cvar::Query("skate3_ultrawide_target_aspect")); - ImGui::SameLine(); - ImGui::Text("Export: %s", export_path_.string().c_str()); - - if (ImGui::Button("Enable screen reads")) { - graphics::ultrawide_debug::SetAllScreenCallbacksEnabled(true); - } - ImGui::SameLine(); - if (ImGui::Button("Disable screen reads")) { - graphics::ultrawide_debug::SetAllScreenCallbacksEnabled(false); - } - - if (ImGui::BeginTable("screen_callbacks", 8, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit, - ImVec2(0.0f, 130.0f))) { - ImGui::TableSetupColumn("Spoof", ImGuiTableColumnFlags_WidthFixed, 54.0f); - ImGui::TableSetupColumn("Kind", ImGuiTableColumnFlags_WidthFixed, 86.0f); - ImGui::TableSetupColumn("Site", ImGuiTableColumnFlags_WidthFixed, 94.0f); - ImGui::TableSetupColumn("Native", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Returned", ImGuiTableColumnFlags_WidthFixed, 78.0f); - ImGui::TableSetupColumn("Spoofed", ImGuiTableColumnFlags_WidthFixed, 62.0f); - ImGui::TableSetupColumn("Calls", ImGuiTableColumnFlags_WidthFixed, 82.0f); - ImGui::TableSetupColumn("Hash", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableHeadersRow(); - for (const auto& entry : screen_callbacks) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool enabled = entry.enabled; - const std::string checkbox_id = "##screen_" + Hex(entry.hash, 16); - if (ImGui::Checkbox(checkbox_id.c_str(), &enabled)) { - graphics::ultrawide_debug::SetScreenCallbackEnabled(entry.hash, enabled); - } - ImGui::TableNextColumn(); - ImGui::TextUnformatted(ScreenCallbackKindLabel(entry.kind)); - ImGui::TableNextColumn(); - const std::string lr = Hex(entry.caller_lr, 8); - ImGui::TextUnformatted(lr.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u", entry.last_value); - ImGui::TableNextColumn(); - ImGui::Text("%u", entry.last_returned_value); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.last_spoofed ? "yes" : "no"); - ImGui::TableNextColumn(); - ImGui::Text("%llu", static_cast(entry.call_count)); - ImGui::TableNextColumn(); - const std::string hash = Hex(entry.hash, 16); - ImGui::TextUnformatted(hash.c_str()); - } - ImGui::EndTable(); - } - - const ImGuiTableFlags table_flags = - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | - ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit; - if (ImGui::BeginTable("ultrawide_targets", 13, table_flags, ImVec2(0.0f, 0.0f))) { - ImGui::TableSetupColumn("Apply", ImGuiTableColumnFlags_WidthFixed, 54.0f); - ImGui::TableSetupColumn("Default", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Shadow", ImGuiTableColumnFlags_WidthFixed, 58.0f); - ImGui::TableSetupColumn("Group", ImGuiTableColumnFlags_WidthFixed, 285.0f); - ImGui::TableSetupColumn("Hash", ImGuiTableColumnFlags_WidthFixed, 132.0f); - ImGui::TableSetupColumn("RT0"); - ImGui::TableSetupColumn("Depth"); - ImGui::TableSetupColumn("Surface"); - ImGui::TableSetupColumn("Viewport", ImGuiTableColumnFlags_WidthFixed, 118.0f); - ImGui::TableSetupColumn("DepthCtl"); - ImGui::TableSetupColumn("VTE"); - ImGui::TableSetupColumn("VS"); - ImGui::TableSetupColumn("Draws"); - ImGui::TableHeadersRow(); - - for (const auto& target : targets) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - bool enabled = target.enabled; - const std::string checkbox_id = "##apply_" + Hex(target.hash, 16); - if (ImGui::Checkbox(checkbox_id.c_str(), &enabled)) { - graphics::ultrawide_debug::SetTargetEnabled(target.hash, enabled); - } - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(target.default_enabled ? "yes" : "no"); - ImGui::TableNextColumn(); - ImGui::TextUnformatted(target.shadow_candidate ? "yes" : "no"); - ImGui::TableNextColumn(); - const std::string group = TargetGroupLabel(target); - ImGui::TextUnformatted(group.c_str()); - ImGui::TableNextColumn(); - const std::string hash = Hex(target.hash, 16); - ImGui::TextUnformatted(hash.c_str()); - ImGui::TableNextColumn(); - const std::string rt0 = Hex(target.key.color_info[0], 8); - ImGui::TextUnformatted(rt0.c_str()); - ImGui::TableNextColumn(); - const std::string depth = Hex(target.key.depth_info, 8); - ImGui::TextUnformatted(depth.c_str()); - ImGui::TableNextColumn(); - const std::string surface = Hex(target.key.surface_info, 8); - ImGui::TextUnformatted(surface.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%u,%u %ux%u", target.key.viewport_x, target.key.viewport_y, - target.key.viewport_width, target.key.viewport_height); - ImGui::TableNextColumn(); - const std::string depth_control = Hex(target.key.depth_control, 8); - ImGui::TextUnformatted(depth_control.c_str()); - ImGui::TableNextColumn(); - const std::string vte = Hex(target.key.pa_cl_vte_cntl, 8); - ImGui::TextUnformatted(vte.c_str()); - ImGui::TableNextColumn(); - const std::string vs = Hex(target.key.vertex_shader_hash, 16); - ImGui::TextUnformatted(vs.c_str()); - ImGui::TableNextColumn(); - ImGui::Text("%llu / %llu", static_cast(target.applied_draw_count), - static_cast(target.draw_count)); - } - ImGui::EndTable(); - } - - ImGui::End(); - if (!open) { - Hide(); - } -} - -} // namespace rex::ui