add Skate 3 ultrawide runtime support

This commit is contained in:
mchughalex 2026-06-09 21:33:53 +01:00
parent 34accdd241
commit 05049e0769
24 changed files with 4344 additions and 15 deletions

View file

@ -274,6 +274,7 @@ class CommandProcessor {
uint32_t primary_buffer_ptr_ = 0; uint32_t primary_buffer_ptr_ = 0;
uint32_t primary_buffer_size_ = 0; uint32_t primary_buffer_size_ = 0;
uint32_t current_packet_ptr_ = 0;
uint32_t read_ptr_index_ = 0; uint32_t read_ptr_index_ = 0;
uint32_t read_ptr_update_freq_ = 0; uint32_t read_ptr_update_freq_ = 0;

View file

@ -369,9 +369,11 @@ class D3D12CommandProcessor : public CommandProcessor {
const draw_util::Scissor& scissor, bool primitive_polygonal, const draw_util::Scissor& scissor, bool primitive_polygonal,
reg::RB_DEPTHCONTROL normalized_depth_control); reg::RB_DEPTHCONTROL normalized_depth_control);
void UpdateSystemConstantValues(bool shared_memory_is_uav, bool primitive_polygonal, void UpdateSystemConstantValues(bool shared_memory_is_uav, bool primitive_polygonal,
Shader::HostVertexShaderType host_vertex_shader_type,
uint32_t line_loop_closing_index, xenos::Endian index_endian, uint32_t line_loop_closing_index, xenos::Endian index_endian,
const draw_util::ViewportInfo& viewport_info, const draw_util::ViewportInfo& viewport_info,
uint32_t used_texture_mask, uint32_t used_texture_mask,
uint64_t vertex_shader_hash, uint64_t pixel_shader_hash,
reg::RB_DEPTHCONTROL normalized_depth_control, reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask); uint32_t normalized_color_mask);
bool UpdateBindings(const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader, bool UpdateBindings(const D3D12Shader* vertex_shader, const D3D12Shader* pixel_shader,

View file

@ -125,6 +125,21 @@ class TextureCache {
bool& signed_view_out, bool& signed_view_out,
uint32_t* texture_base_out = nullptr, uint32_t* texture_base_out = nullptr,
uint32_t* texture_length_out = nullptr) const; uint32_t* texture_length_out = nullptr) const;
struct DebugActiveTextureBinding {
uint64_t key_hash = 0;
uint32_t fetch_index = 0;
uint32_t base_address = 0;
uint32_t base_length = 0;
uint32_t mip_address = 0;
uint32_t mip_length = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth_or_array_size = 0;
uint32_t format = 0;
bool scaled_resolve = false;
};
bool DebugGetActiveTextureBinding(uint32_t fetch_constant_index,
DebugActiveTextureBinding& binding_out) const;
// "ActiveTexture" means as of the latest RequestTextures call. // "ActiveTexture" means as of the latest RequestTextures call.
@ -590,6 +605,10 @@ class TextureCache {
static bool DebugIsTeamProfileBackgroundCandidateKey(const TextureKey& key); static bool DebugIsTeamProfileBackgroundCandidateKey(const TextureKey& key);
void DebugLogTeamProfileBackgroundTextureCandidate(const char* action, uint32_t fetch_index, void DebugLogTeamProfileBackgroundTextureCandidate(const char* action, uint32_t fetch_index,
const Texture& texture); 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); void UpdateTexturesTotalHostMemoryUsage(uint64_t add, uint64_t subtract);

View file

@ -0,0 +1,180 @@
#pragma once
#include <array>
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
namespace rex::graphics::ultrawide_debug {
struct TargetKey {
std::array<uint32_t, 4> 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();
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<TargetEntry> 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<ScreenCallbackEntry> 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<GuestStoreWatchEntry> 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<DrawFingerprintEntry> 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<std::filesystem::path> LoadTargets(const std::filesystem::path& path);
std::optional<std::filesystem::path> SaveTargets(const std::filesystem::path& path);
} // namespace rex::graphics::ultrawide_debug

View file

@ -514,6 +514,7 @@ class VulkanCommandProcessor : public CommandProcessor {
const PrimitiveProcessor::ProcessingResult& primitive_processing_result, const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
bool shader_32bit_index_dma, uint32_t compute_memexport_vertex_count, bool shader_32bit_index_dma, uint32_t compute_memexport_vertex_count,
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask, const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
uint64_t vertex_shader_hash, uint64_t pixel_shader_hash,
reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask); reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask);
bool UpdateBindings(const VulkanShader* vertex_shader, const VulkanShader* pixel_shader); bool UpdateBindings(const VulkanShader* vertex_shader, const VulkanShader* pixel_shader);
// Allocates a descriptor set and fills one or two VkWriteDescriptorSet // Allocates a descriptor set and fills one or two VkWriteDescriptorSet

View file

@ -40,6 +40,20 @@ inline u32 PhysicalHostOffset(u32 guest_addr) noexcept {
namespace rex::memory { namespace rex::memory {
struct PhysicalMemoryInvalidationDiagnostics {
bool valid = false;
uint32_t virtual_address = 0;
uint32_t physical_address = 0;
uint32_t length = 0;
uint32_t thread_id = 0xFFFFFFFF;
uint32_t lr = 0;
uint32_t stack = 0;
bool exact_range = false;
bool unprotect = false;
};
const PhysicalMemoryInvalidationDiagnostics& GetPhysicalMemoryInvalidationDiagnostics();
/// Lightweight guest-to-host pointer translation using the memory base. /// Lightweight guest-to-host pointer translation using the memory base.
/// For hooks and kernel code operating with the base pointer directly. /// For hooks and kernel code operating with the base pointer directly.
/// Same raw arithmetic as recompiled code; no Memory* or heap lookup needed. /// Same raw arithmetic as recompiled code; no Memory* or heap lookup needed.

View file

@ -68,6 +68,8 @@ class SimpleSettingsDialog final : public ImGuiDialog {
bool visible_ = false; bool visible_ = false;
int resolution_scale_index_ = 0; int resolution_scale_index_ = 0;
int frame_cap_index_ = 0; int frame_cap_index_ = 0;
int aspect_ratio_index_ = 0;
float field_of_view_ = 60.0f;
bool fullscreen_ = true; bool fullscreen_ = true;
bool vsync_ = false; bool vsync_ = false;
bool tearing_ = true; bool tearing_ = true;

View file

@ -0,0 +1,36 @@
#pragma once
#include <filesystem>
#include <functional>
#include <rex/ui/imgui_dialog.h>
namespace rex::ui {
class UltrawideTargetsDialog final : public ImGuiDialog {
public:
using CloseCallback = std::function<void()>;
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

View file

@ -660,6 +660,14 @@ class Presenter {
uint32_t host_rt_width, uint32_t host_rt_height, uint32_t host_rt_width, uint32_t host_rt_height,
uint32_t max_rt_width, uint32_t max_rt_height, uint32_t max_rt_width, uint32_t max_rt_height,
const GuestOutputPaintConfig& config) const; const GuestOutputPaintConfig& config) const;
public:
bool GetSurfacePaintConnectionSize(uint32_t& width_out, uint32_t& height_out) const {
width_out = surface_width_in_paint_connection_;
height_out = surface_height_in_paint_connection_;
return width_out && height_out;
}
protected:
// is_8bpc_out_ref is where to write whether the source actually has no more // is_8bpc_out_ref is where to write whether the source actually has no more
// than 8 bits of precision per channel (though the image provided by the // than 8 bits of precision per channel (though the image provided by the
// refresher may still have a higher storage precision) - if not written, it // refresher may still have a higher storage precision) - if not written, it

View file

@ -20,6 +20,7 @@ add_library(rexcore OBJECT
string_buffer.cpp string_buffer.cpp
threading.cpp threading.cpp
timer_queue.cpp timer_queue.cpp
ultrawide_debug.cpp
utf8.cpp utf8.cpp
vec128.cpp vec128.cpp
) )

View file

@ -694,7 +694,9 @@ bool ShouldCollectDrawDiagnostics() {
} }
bool ShouldCollectDrawFingerprints() { bool ShouldCollectDrawFingerprints() {
return REXCVAR_GET(perf_draw_fingerprints) || ShouldCollectDrawDiagnostics(); return REXCVAR_GET(perf_draw_fingerprints) ||
rex::cvar::Query<bool>("skate3_ultrawide_trace_draws") ||
ShouldCollectDrawDiagnostics();
} }
int64_t GetCounter(CounterId id) { int64_t GetCounter(CounterId id) {

View file

@ -0,0 +1,184 @@
// 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},
};

1526
src/core/ultrawide_debug.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,10 @@
*/ */
#include <algorithm> #include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <mutex>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -30,13 +34,102 @@ REXCVAR_DEFINE_INT32(filesystem_debug_log_fe_asset_ops_remaining, 0, "Filesystem
.lifecycle(rex::cvar::Lifecycle::kHotReload) .lifecycle(rex::cvar::Lifecycle::kHotReload)
.debug_only(); .debug_only();
REXCVAR_DEFINE_INT32(filesystem_debug_log_team_profile_background_remaining, 200, "Filesystem", REXCVAR_DEFINE_INT32(filesystem_debug_log_team_profile_background_remaining, 0, "Filesystem",
"Log team_profile_background_0.rx2 file opens and reads for this many " "Log team_profile_background_0.rx2 file opens and reads for this many "
"operations") "operations")
.range(0, 10000) .range(0, 10000)
.lifecycle(rex::cvar::Lifecycle::kHotReload) .lifecycle(rex::cvar::Lifecycle::kHotReload)
.debug_only(); .debug_only();
REXCVAR_DEFINE_BOOL(filesystem_debug_log_chan_center_stream_files, false, "Filesystem",
"Log focused Chan Center world chunk file opens to disk")
.lifecycle(rex::cvar::Lifecycle::kHotReload)
.debug_only();
namespace {
bool IsSkate3ChanCenterStreamPath(std::string_view path) {
constexpr std::string_view kNeedles[] = {
"DIST_University", "cTex_500_-300_high", "cTex_500_-200_high",
"cTex_100_200_high", "cPres_350_-50_high", "cPres_350_-350_high",
"cPres_550_-150_high", "cSim_150_-350_high",
};
for (std::string_view needle : kNeedles) {
if (path.find(needle) != std::string_view::npos) {
return true;
}
}
return false;
}
std::filesystem::path Skate3ChanCenterCacheLogPath(const char* filename) {
#if defined(_WIN32)
char* appdata_raw = nullptr;
size_t appdata_length = 0;
if (_dupenv_s(&appdata_raw, &appdata_length, "APPDATA") == 0 && appdata_raw &&
appdata_length > 0) {
std::filesystem::path appdata_path(appdata_raw);
std::free(appdata_raw);
return appdata_path / "skate3" / "cache" / filename;
}
std::free(appdata_raw);
#else
if (const char* appdata = std::getenv("APPDATA")) {
return std::filesystem::path(appdata) / "skate3" / "cache" / filename;
}
#endif
return filename;
}
void LogSkate3ChanCenterStreamFileOpen(std::string_view stage, std::string_view path,
std::string_view absolute_path, uint32_t result,
uint32_t action, uint32_t desired_access,
uint32_t creation_disposition) {
if (!REXCVAR_GET(filesystem_debug_log_chan_center_stream_files) ||
!IsSkate3ChanCenterStreamPath(path)) {
return;
}
static std::mutex log_mutex;
static std::ofstream log_file;
static uint64_t sequence = 0;
std::lock_guard lock(log_mutex);
if (!log_file.is_open()) {
std::filesystem::path log_path =
Skate3ChanCenterCacheLogPath("skate3_chan_center_file_trace.log");
std::error_code ec;
if (log_path.has_parent_path()) {
std::filesystem::create_directories(log_path.parent_path(), ec);
}
log_file.open(log_path, std::ios::out | std::ios::trunc);
if (log_file.is_open()) {
log_file << "seq,stage,result,action,access,disposition,path,absolute\n";
}
}
if (!log_file.is_open()) {
return;
}
auto write_escaped = [&](std::string_view text) {
for (char ch : text) {
log_file << (ch == ',' ? ';' : ch);
}
};
log_file << std::dec << sequence++ << ',' << stage << ",0x" << std::hex << result << ",0x"
<< action << ",0x" << desired_access << ",0x" << creation_disposition << ',';
write_escaped(path);
log_file << ',';
write_escaped(absolute_path);
log_file << '\n';
log_file.flush();
REXFS_WARN("Skate3 Chan stream file: stage={} result={:#x} action={:#x} path='{}' absolute='{}'",
stage, result, action, path, absolute_path);
}
} // namespace
namespace rex::filesystem { namespace rex::filesystem {
namespace { namespace {
@ -283,6 +376,9 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
parent_entry = !root_entry ? ResolvePath(base_path) : root_entry->ResolvePath(base_path); parent_entry = !root_entry ? ResolvePath(base_path) : root_entry->ResolvePath(base_path);
if (!parent_entry) { if (!parent_entry) {
*out_action = FileAction::kDoesNotExist; *out_action = FileAction::kDoesNotExist;
LogSkate3ChanCenterStreamFileOpen("missing-parent", path, {}, X_STATUS_NO_SUCH_FILE,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }
@ -303,6 +399,10 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
if (entry) { if (entry) {
if (entry->attributes() & kFileAttributeDirectory && is_non_directory) { if (entry->attributes() & kFileAttributeDirectory && is_non_directory) {
LogSkate3ChanCenterStreamFileOpen("is-directory", path, entry->absolute_path(),
X_STATUS_FILE_IS_A_DIRECTORY,
static_cast<uint32_t>(FileAction::kOpened), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_FILE_IS_A_DIRECTORY; return X_STATUS_FILE_IS_A_DIRECTORY;
} }
@ -326,6 +426,9 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
// Must exist. // Must exist.
if (!entry) { if (!entry) {
*out_action = FileAction::kDoesNotExist; *out_action = FileAction::kDoesNotExist;
LogSkate3ChanCenterStreamFileOpen("missing-entry", path, {}, X_STATUS_NO_SUCH_FILE,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_NO_SUCH_FILE; return X_STATUS_NO_SUCH_FILE;
} }
break; break;
@ -333,6 +436,10 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
// Must not exist. // Must not exist.
if (entry) { if (entry) {
*out_action = FileAction::kExists; *out_action = FileAction::kExists;
LogSkate3ChanCenterStreamFileOpen(
"collision", path, entry->absolute_path(), X_STATUS_OBJECT_NAME_COLLISION,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_OBJECT_NAME_COLLISION; return X_STATUS_OBJECT_NAME_COLLISION;
} }
break; break;
@ -359,10 +466,18 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
case FileDisposition::kCreate: case FileDisposition::kCreate:
// Shouldn't be possible to hit this. // Shouldn't be possible to hit this.
assert_always(); assert_always();
LogSkate3ChanCenterStreamFileOpen(
"invalid-create", path, entry->absolute_path(), X_STATUS_ACCESS_DENIED,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
case FileDisposition::kSuperscede: case FileDisposition::kSuperscede:
// Replace (by delete + recreate). // Replace (by delete + recreate).
if (!entry->Delete()) { if (!entry->Delete()) {
LogSkate3ChanCenterStreamFileOpen(
"delete-denied", path, entry->absolute_path(), X_STATUS_ACCESS_DENIED,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }
entry = nullptr; entry = nullptr;
@ -380,6 +495,10 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
if (entry->Delete()) { if (entry->Delete()) {
entry = nullptr; entry = nullptr;
} else if (!entry->Truncate()) { } else if (!entry->Truncate()) {
LogSkate3ChanCenterStreamFileOpen(
"truncate-denied", path, entry->absolute_path(), X_STATUS_ACCESS_DENIED,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }
*out_action = FileAction::kOverwritten; *out_action = FileAction::kOverwritten;
@ -390,6 +509,9 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
// Create if needed (either new or as a replacement). // Create if needed (either new or as a replacement).
entry = CreatePath(path, !is_directory ? kFileAttributeNormal : kFileAttributeDirectory); entry = CreatePath(path, !is_directory ? kFileAttributeNormal : kFileAttributeDirectory);
if (!entry) { if (!entry) {
LogSkate3ChanCenterStreamFileOpen("create-denied", path, {}, X_STATUS_ACCESS_DENIED,
static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
return X_STATUS_ACCESS_DENIED; return X_STATUS_ACCESS_DENIED;
} }
} }
@ -410,6 +532,9 @@ X_STATUS VirtualFileSystem::OpenFile(Entry* root_entry, const std::string_view p
path, desired_access, static_cast<int>(creation_disposition), static_cast<int>(*out_action), path, desired_access, static_cast<int>(creation_disposition), static_cast<int>(*out_action),
result, entry ? entry->absolute_path() : std::string_view("<null>")); result, entry ? entry->absolute_path() : std::string_view("<null>"));
} }
LogSkate3ChanCenterStreamFileOpen("open", path, entry ? entry->absolute_path() : std::string_view{},
result, static_cast<uint32_t>(*out_action), desired_access,
static_cast<uint32_t>(creation_disposition));
if (XFAILED(result)) { if (XFAILED(result)) {
*out_action = FileAction::kDoesNotExist; *out_action = FileAction::kDoesNotExist;
} }

View file

@ -885,11 +885,13 @@ bool CommandProcessor::ExecutePacketType3(memory::RingBuffer* reader, uint32_t p
return false; return false;
} }
current_packet_ptr_ = uint32_t(reader->read_ptr() - 4);
// To handle nesting behavior when tracing we special case indirect buffers. // To handle nesting behavior when tracing we special case indirect buffers.
if (opcode == PM4_INDIRECT_BUFFER) { if (opcode == PM4_INDIRECT_BUFFER) {
trace_writer_.WritePacketStart(uint32_t(reader->read_ptr() - 4), 2); trace_writer_.WritePacketStart(current_packet_ptr_, 2);
} else { } else {
trace_writer_.WritePacketStart(uint32_t(reader->read_ptr() - 4), 1 + count); trace_writer_.WritePacketStart(current_packet_ptr_, 1 + count);
} }
// & 1 == predicate - when set, we do bin check to see if we should execute // & 1 == predicate - when set, we do bin check to see if we should execute

View file

@ -25,6 +25,7 @@
#include <rex/graphics/d3d12/shader.h> #include <rex/graphics/d3d12/shader.h>
#include <rex/graphics/flags.h> #include <rex/graphics/flags.h>
#include <rex/graphics/registers.h> #include <rex/graphics/registers.h>
#include <rex/graphics/ultrawide_debug.h>
#include <rex/graphics/util/draw.h> #include <rex/graphics/util/draw.h>
#include <rex/graphics/xenos.h> #include <rex/graphics/xenos.h>
#include <rex/kernel/xboxkrnl/video.h> #include <rex/kernel/xboxkrnl/video.h>
@ -49,6 +50,23 @@ REXCVAR_DEFINE_BOOL(d3d12_submit_on_primary_buffer_end, true, "GPU/D3D12",
"Submit command list when PM4 primary buffer ends") "Submit command list when PM4 primary buffer ends")
.lifecycle(rex::cvar::Lifecycle::kHotReload); .lifecycle(rex::cvar::Lifecycle::kHotReload);
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_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 rex::graphics::d3d12 {
namespace { namespace {
@ -61,6 +79,142 @@ bool IsGameplayStateActive(const system::KernelState* kernel_state) {
return gameplay_context && *gameplay_context == 1; 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<bool>("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<double>("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<int32_t>("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<double>("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<bool>("skate3_ultrawide") &&
rex::cvar::Query<bool>("skate3_ultrawide_hor_plus") &&
ultrawide_debug::IsSkate3GameplayUltrawideActive() &&
!rex::cvar::Query<bool>("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<bool>("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;
}
#ifdef REXGLUE_ENABLE_PERF_COUNTERS #ifdef REXGLUE_ENABLE_PERF_COUNTERS
class ScopedDrawStageTimer { class ScopedDrawStageTimer {
public: public:
@ -222,6 +376,45 @@ void D3D12CommandProcessor::RestoreEdramSnapshot(const void* snapshot) {
bool D3D12CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(memory::RingBuffer* reader, bool D3D12CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(memory::RingBuffer* reader,
uint32_t packet, uint32_t count) { uint32_t packet, uint32_t count) {
auto write_fake_result = [this, reader, count](int32_t fake_sample_count) -> bool {
// Set by D3D as BE but struct ABI is LE.
const uint32_t kQueryFinished = rex::byte_swap(0xFFFFFEED);
assert_true(count == 1);
uint32_t initiator = reader->ReadAndSwap<uint32_t>();
WriteRegister(XE_GPU_REG_VGT_EVENT_INITIATOR, initiator & 0x3F);
uint32_t sample_count_addr = register_file_->values[XE_GPU_REG_RB_SAMPLE_COUNT_ADDR];
auto* sample_counts =
memory_->TranslatePhysical<xenos::xe_gpu_depth_sample_counts*>(sample_count_addr);
if (!sample_counts) {
return true;
}
bool is_end_via_z_pass =
sample_counts->ZPass_A == kQueryFinished && sample_counts->ZPass_B == kQueryFinished;
bool is_end_via_z_fail =
sample_counts->ZFail_A == kQueryFinished && sample_counts->ZFail_B == kQueryFinished;
std::memset(sample_counts, 0, sizeof(xenos::xe_gpu_depth_sample_counts));
if (is_end_via_z_pass || is_end_via_z_fail) {
sample_counts->ZPass_A = fake_sample_count;
sample_counts->Total_A = fake_sample_count;
}
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));
}
if (!REXCVAR_GET(occlusion_query_enable) || !occlusion_query_resources_available_) { if (!REXCVAR_GET(occlusion_query_enable) || !occlusion_query_resources_available_) {
return CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(reader, packet, count); return CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(reader, packet, count);
} }
@ -2107,6 +2300,7 @@ void D3D12CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbu
kernel::xboxkrnl::VdQueryVideoMode(&video_mode); kernel::xboxkrnl::VdQueryVideoMode(&video_mode);
uint32_t display_width = std::max(uint32_t(1), uint32_t(video_mode.display_width)); 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)); uint32_t display_height = std::max(uint32_t(1), uint32_t(video_mode.display_height));
ApplySkate3UltrawidePresenterAspect(presenter, display_width, display_height);
presenter->RefreshGuestOutput( presenter->RefreshGuestOutput(
guest_output_width, guest_output_height, display_width, display_height, guest_output_width, guest_output_height, display_width, display_height,
@ -2622,9 +2816,12 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, uint3
// Update system constants before uploading them. // Update system constants before uploading them.
// TODO(Triang3l): With ROV, pass the disabled render target mask for safety. // TODO(Triang3l): With ROV, pass the disabled render target mask for safety.
UpdateSystemConstantValues(memexport_used, primitive_polygonal, UpdateSystemConstantValues(memexport_used, primitive_polygonal,
primitive_processing_result.host_vertex_shader_type,
primitive_processing_result.line_loop_closing_index, primitive_processing_result.line_loop_closing_index,
primitive_processing_result.host_shader_index_endian, viewport_info, primitive_processing_result.host_shader_index_endian, viewport_info,
used_texture_mask, normalized_depth_control, normalized_color_mask); used_texture_mask, vertex_shader->ucode_data_hash(),
pixel_shader ? pixel_shader->ucode_data_hash() : 0,
normalized_depth_control, normalized_color_mask);
} }
// Update constant buffers, descriptors and root parameters. // Update constant buffers, descriptors and root parameters.
@ -2783,6 +2980,69 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type, uint3
// Must not call anything that may change the primitive topology from now on! // 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 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;
if (rex::cvar::Query<bool>("skate3_ultrawide_trace_draws")) {
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;
}
#ifdef REXGLUE_ENABLE_PERF_COUNTERS #ifdef REXGLUE_ENABLE_PERF_COUNTERS
const uint32_t host_draw_primitive_count = draw_util::EstimatePrimitiveCount( const uint32_t host_draw_primitive_count = draw_util::EstimatePrimitiveCount(
primitive_processing_result.host_primitive_type, host_draw_vertex_count); primitive_processing_result.host_primitive_type, host_draw_vertex_count);
@ -3937,9 +4197,11 @@ void D3D12CommandProcessor::UpdateFixedFunctionState(
} }
void D3D12CommandProcessor::UpdateSystemConstantValues( void D3D12CommandProcessor::UpdateSystemConstantValues(
bool shared_memory_is_uav, bool primitive_polygonal, uint32_t line_loop_closing_index, bool shared_memory_is_uav, bool primitive_polygonal,
Shader::HostVertexShaderType host_vertex_shader_type, uint32_t line_loop_closing_index,
xenos::Endian index_endian, const draw_util::ViewportInfo& viewport_info, xenos::Endian index_endian, const draw_util::ViewportInfo& viewport_info,
uint32_t used_texture_mask, reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t used_texture_mask, uint64_t vertex_shader_hash, uint64_t pixel_shader_hash,
reg::RB_DEPTHCONTROL normalized_depth_control,
uint32_t normalized_color_mask) { uint32_t normalized_color_mask) {
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES #if XE_GPU_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu"); SCOPE_profile_cpu_f("gpu");
@ -4129,10 +4391,37 @@ void D3D12CommandProcessor::UpdateSystemConstantValues(
} }
// Conversion to Direct3D 12 normalized device coordinates. // 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) { for (uint32_t i = 0; i < 3; ++i) {
dirty |= system_constants_.ndc_scale[i] != viewport_info.ndc_scale[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_offset[i] != viewport_info.ndc_offset[i]; dirty |= system_constants_.ndc_offset[i] != viewport_info.ndc_offset[i];
system_constants_.ndc_scale[i] = viewport_info.ndc_scale[i]; system_constants_.ndc_scale[i] = ndc_scale;
system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i]; system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i];
} }

View file

@ -24,10 +24,12 @@
#include <rex/graphics/pipeline/texture/info.h> #include <rex/graphics/pipeline/texture/info.h>
#include <rex/graphics/pipeline/texture/util.h> #include <rex/graphics/pipeline/texture/util.h>
#include <rex/graphics/register_file.h> #include <rex/graphics/register_file.h>
#include <rex/graphics/ultrawide_debug.h>
#include <rex/graphics/xenos.h> #include <rex/graphics/xenos.h>
#include <rex/logging.h> #include <rex/logging.h>
#include <rex/math.h> #include <rex/math.h>
#include <rex/perf/counter.h> #include <rex/perf/counter.h>
#include <rex/system/xmemory.h>
REXCVAR_DEFINE_INT32(texture_cache_memory_limit_render_to_texture, 128, "GPU", REXCVAR_DEFINE_INT32(texture_cache_memory_limit_render_to_texture, 128, "GPU",
"Texture cache memory limit for render-to-texture (MB)") "Texture cache memory limit for render-to-texture (MB)")
@ -106,6 +108,25 @@ REXCVAR_DEFINE_INT32(vulkan_debug_log_team_profile_background_bindings_remaining
.lifecycle(rex::cvar::Lifecycle::kHotReload) .lifecycle(rex::cvar::Lifecycle::kHotReload)
.debug_only(); .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", REXCVAR_DEFINE_BOOL(pre_mask_resolve_l2_block, true, "GPU",
"Pre-mask scaled resolve L2 blocks to the write range before iterating"); "Pre-mask scaled resolve L2 blocks to the write range before iterating");
@ -323,6 +344,9 @@ void TextureCache::CompletedSubmissionUpdated(uint64_t completed_submission_inde
assert_true(found_texture_it != textures_.end()); assert_true(found_texture_it != textures_.end());
if (found_texture_it != textures_.end()) { if (found_texture_it != textures_.end()) {
assert_true(found_texture_it->second.get() == texture); 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); textures_.erase(found_texture_it);
// `texture` is invalid now. // `texture` is invalid now.
} }
@ -493,6 +517,11 @@ bool TextureCache::CommitPreparedTextureLoad(const PendingTextureLoad& pending_l
// not up to date anymore. // not up to date anymore.
texture.MakeUpToDateAndWatch(global_critical_region_.Acquire()); texture.MakeUpToDateAndWatch(global_critical_region_.Acquire());
texture.LogAction("Loaded"); 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); DebugLogTeamProfileBackgroundTextureCandidate("loaded", UINT32_MAX, texture);
return true; return true;
@ -569,6 +598,7 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
if (!binding.key.is_valid) { if (!binding.key.is_valid) {
if (old_key.is_valid) { if (old_key.is_valid) {
bindings_changed |= index_bit; bindings_changed |= index_bit;
DebugLogSkate3UltrawideTextureTrace("unbind-invalid-fetch", index, old_key);
} }
binding.Reset(); binding.Reset();
continue; continue;
@ -630,6 +660,15 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
if (load_signed_data) { if (load_signed_data) {
queue_pending_texture_load(binding.texture_signed); 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", COUNT_profile_set("gpu/texture_cache/request_textures_pending_load_count",
@ -691,6 +730,36 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
DebugLogScaledTextureBindings(used_texture_mask); DebugLogScaledTextureBindings(used_texture_mask);
} }
bool TextureCache::DebugGetActiveTextureBinding(
uint32_t fetch_constant_index, DebugActiveTextureBinding& binding_out) const {
binding_out = {};
if (fetch_constant_index >= texture_bindings_.size()) {
return false;
}
const TextureBinding* binding = GetValidTextureBinding(fetch_constant_index);
if (binding == nullptr) {
return false;
}
const TextureKey& key = binding->key;
binding_out.key_hash = uint64_t(TextureKey::Hasher{}(key));
binding_out.fetch_index = fetch_constant_index;
binding_out.base_address = key.base_page << 12;
binding_out.mip_address = key.mip_page << 12;
binding_out.width = key.GetWidth();
binding_out.height = key.GetHeight();
binding_out.depth_or_array_size = key.GetDepthOrArraySize();
binding_out.format = uint32_t(key.format);
binding_out.scaled_resolve = key.scaled_resolve != 0;
const Texture* texture = binding->texture ? binding->texture : binding->texture_signed;
if (texture != nullptr) {
binding_out.base_length = texture->GetGuestBaseSize();
binding_out.mip_length = texture->GetGuestMipsSize();
}
return true;
}
void TextureCache::DebugRecordResolveReadback(uint32_t start, uint32_t length, bool scaled) { void TextureCache::DebugRecordResolveReadback(uint32_t start, uint32_t length, bool scaled) {
if (!length) { if (!length) {
return; return;
@ -821,6 +890,90 @@ bool TextureCache::DebugFindTeamProfileBackgroundBinding(uint32_t used_texture_m
return false; 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, void TextureCache::DebugLogTeamProfileBackgroundTextureCandidate(const char* action,
uint32_t fetch_index, uint32_t fetch_index,
const Texture& texture) { const Texture& texture) {
@ -1216,6 +1369,26 @@ void TextureCache::Texture::MarkAsUsed() {
void TextureCache::Texture::WatchCallback( void TextureCache::Texture::WatchCallback(
[[maybe_unused]] const std::unique_lock<std::recursive_mutex>& global_lock, bool is_mip, [[maybe_unused]] const std::unique_lock<std::recursive_mutex>& global_lock, bool is_mip,
bool invalidated_by_gpu) { 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( texture_cache().DebugLogTeamProfileBackgroundTextureCandidate(
invalidated_by_gpu ? (is_mip ? "invalidate-gpu-mips" : "invalidate-gpu-base") invalidated_by_gpu ? (is_mip ? "invalidate-gpu-mips" : "invalidate-gpu-base")
: (is_mip ? "invalidate-cpu-mips" : "invalidate-cpu-base"), : (is_mip ? "invalidate-cpu-mips" : "invalidate-cpu-base"),
@ -1243,6 +1416,11 @@ void TextureCache::WatchCallback(const std::unique_lock<std::recursive_mutex>& g
void TextureCache::DestroyAllTextures(bool from_destructor) { void TextureCache::DestroyAllTextures(bool from_destructor) {
ResetTextureBindings(from_destructor); ResetTextureBindings(from_destructor);
if (!from_destructor) {
for (const auto& [key, texture] : textures_) {
DebugLogSkate3UltrawideTextureTrace("destroy-clear", UINT32_MAX, key, texture.get());
}
}
textures_.clear(); textures_.clear();
COUNT_profile_set("gpu/texture_cache/textures", 0); COUNT_profile_set("gpu/texture_cache/textures", 0);
} }
@ -1365,6 +1543,7 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
} }
COUNT_profile_set("gpu/texture_cache/textures", textures_.size()); COUNT_profile_set("gpu/texture_cache/textures", textures_.size());
texture->LogAction("Created"); texture->LogAction("Created");
DebugLogSkate3UltrawideTextureTrace("create", UINT32_MAX, texture->key(), texture);
DebugLogTeamProfileBackgroundTextureCandidate("create", UINT32_MAX, *texture); DebugLogTeamProfileBackgroundTextureCandidate("create", UINT32_MAX, *texture);
return texture; return texture;
} }

View file

@ -39,6 +39,7 @@
#include <rex/graphics/pipeline/shader/shader.h> #include <rex/graphics/pipeline/shader/shader.h>
#include <rex/graphics/pipeline/shader/spirv_translator.h> #include <rex/graphics/pipeline/shader/spirv_translator.h>
#include <rex/graphics/registers.h> #include <rex/graphics/registers.h>
#include <rex/graphics/ultrawide_debug.h>
#include <rex/graphics/vulkan/command_processor.h> #include <rex/graphics/vulkan/command_processor.h>
#include <rex/graphics/vulkan/pipeline_cache.h> #include <rex/graphics/vulkan/pipeline_cache.h>
#include <rex/graphics/vulkan/render_target_cache.h> #include <rex/graphics/vulkan/render_target_cache.h>
@ -156,6 +157,138 @@ bool IsGameplayStateActive(const system::KernelState* kernel_state) {
return gameplay_context && *gameplay_context == 1; 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<bool>("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<double>("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<int32_t>("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<double>("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<bool>("skate3_ultrawide") &&
rex::cvar::Query<bool>("skate3_ultrawide_hor_plus") &&
ultrawide_debug::IsSkate3GameplayUltrawideActive() &&
!rex::cvar::Query<bool>("skate3_ultrawide_disable_ndc_correction");
if (!state.correction_active) {
return state;
}
state.hor_plus_scale = ComputeSkate3UltrawideHorPlusScale();
state.skip_shadow_targets = rex::cvar::Query<bool>("skate3_ultrawide_skip_shadow_targets");
state.shadow_skip_mode = uint32_t(std::clamp(
rex::cvar::Query<int32_t>("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;
}
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;
}
// Pre-divided XY is used by screen-space and orthographic passes. Widening
// those produces the menu/HUD fragments seen in the side areas.
const bool default_enabled =
host_vertex_shader_type == Shader::HostVertexShaderType::kVertex && primitive_polygonal &&
normalized_depth_control.z_enable && normalized_depth_control.z_write_enable &&
!pa_cl_vte_cntl.vtx_xy_fmt && 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;
}
bool IsInGameContextActive(const system::KernelState* kernel_state) { bool IsInGameContextActive(const system::KernelState* kernel_state) {
if (!kernel_state) { if (!kernel_state) {
return false; return false;
@ -2646,6 +2779,7 @@ void VulkanCommandProcessor::IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontb
kernel::xboxkrnl::VdQueryVideoMode(&video_mode); kernel::xboxkrnl::VdQueryVideoMode(&video_mode);
uint32_t display_width = std::max(uint32_t(1), uint32_t(video_mode.display_width)); 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)); uint32_t display_height = std::max(uint32_t(1), uint32_t(video_mode.display_height));
ApplySkate3UltrawidePresenterAspect(presenter, display_width, display_height);
presenter->RefreshGuestOutput( presenter->RefreshGuestOutput(
guest_output_width, guest_output_height, display_width, display_height, guest_output_width, guest_output_height, display_width, display_height,
@ -4325,6 +4459,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type, uint32_t
// Update system constants before uploading them. // Update system constants before uploading them.
UpdateSystemConstantValues(primitive_polygonal, primitive_processing_result, UpdateSystemConstantValues(primitive_polygonal, primitive_processing_result,
shader_32bit_index_dma, 0, viewport_info, used_texture_mask, shader_32bit_index_dma, 0, viewport_info, used_texture_mask,
vertex_shader->ucode_data_hash(),
pixel_shader ? pixel_shader->ucode_data_hash() : 0,
normalized_depth_control, normalized_color_mask); normalized_depth_control, normalized_color_mask);
// Update uniform buffers and descriptor sets after binding the pipeline with // Update uniform buffers and descriptor sets after binding the pipeline with
@ -4527,6 +4663,67 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type, uint32_t
} }
} }
} }
bool skate3_ultrawide_submit_draw = true;
if (rex::cvar::Query<bool>("skate3_ultrawide_trace_draws")) {
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;
}
// Draw. // Draw.
uint32_t gpu_timestamp_start = BeginGpuTimestampedDraw(draw_bucket); uint32_t gpu_timestamp_start = BeginGpuTimestampedDraw(draw_bucket);
@ -7101,6 +7298,7 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
const PrimitiveProcessor::ProcessingResult& primitive_processing_result, const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
bool shader_32bit_index_dma, uint32_t compute_memexport_vertex_count, bool shader_32bit_index_dma, uint32_t compute_memexport_vertex_count,
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask, const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
uint64_t vertex_shader_hash, uint64_t pixel_shader_hash,
reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask) { reg::RB_DEPTHCONTROL normalized_depth_control, uint32_t normalized_color_mask) {
#if XE_GPU_FINE_GRAINED_DRAW_SCOPES #if XE_GPU_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu"); SCOPE_profile_cpu_f("gpu");
@ -7360,10 +7558,39 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
system_constants_.tessellation_factor_range_max = tessellation_factor_max; system_constants_.tessellation_factor_range_max = tessellation_factor_max;
// Conversion to host normalized device coordinates. // 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) { for (uint32_t i = 0; i < 3; ++i) {
dirty |= system_constants_.ndc_scale[i] != viewport_info.ndc_scale[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_offset[i] != viewport_info.ndc_offset[i]; dirty |= system_constants_.ndc_offset[i] != viewport_info.ndc_offset[i];
system_constants_.ndc_scale[i] = viewport_info.ndc_scale[i]; system_constants_.ndc_scale[i] = ndc_scale;
system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i]; system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i];
} }

View file

@ -10,6 +10,7 @@
*/ */
#include <rex/kernel/xam/apps/xgi_app.h> #include <rex/kernel/xam/apps/xgi_app.h>
#include <rex/graphics/ultrawide_debug.h>
#include <rex/logging.h> #include <rex/logging.h>
#include <rex/thread.h> #include <rex/thread.h>
@ -42,6 +43,8 @@ X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
uint32_t context_value = memory::load_and_swap<uint32_t>(buffer + 20); uint32_t context_value = memory::load_and_swap<uint32_t>(buffer + 20);
auto old_value = kernel_state_->GetUserContext(user_index, context_id); auto old_value = kernel_state_->GetUserContext(user_index, context_id);
kernel_state_->SetUserContext(user_index, context_id, context_value); kernel_state_->SetUserContext(user_index, context_id, context_value);
rex::graphics::ultrawide_debug::NotifySkate3GameplayContext(user_index, context_id,
context_value);
if (!old_value || *old_value != context_value) { if (!old_value || *old_value != context_value) {
REXKRNL_INFO("XGIUserSetContextEx(user={}, context={:#x}, value={:#x})", user_index, REXKRNL_INFO("XGIUserSetContextEx(user={}, context={:#x}, value={:#x})", user_index,
context_id, context_value); context_id, context_value);

View file

@ -19,6 +19,7 @@
#include <rex/graphics/flags.h> #include <rex/graphics/flags.h>
#include <rex/graphics/graphics_system.h> #include <rex/graphics/graphics_system.h>
#include <rex/graphics/pipeline/texture/info.h> #include <rex/graphics/pipeline/texture/info.h>
#include <rex/graphics/ultrawide_debug.h>
#include <rex/graphics/video_mode_util.h> #include <rex/graphics/video_mode_util.h>
#include <rex/graphics/xenos.h> #include <rex/graphics/xenos.h>
#include <rex/kernel/xboxkrnl/private.h> #include <rex/kernel/xboxkrnl/private.h>
@ -38,10 +39,26 @@ namespace {
constexpr uint32_t kDisplayGammaType = 2; constexpr uint32_t kDisplayGammaType = 2;
// Display gamma power (used with gamma type 3) // Display gamma power (used with gamma type 3)
constexpr double kDisplayGammaPower = 2.22222233; constexpr double kDisplayGammaPower = 2.22222233;
constexpr double kSkate3BaseAspect = 16.0 / 9.0;
bool Skate3UltrawidePresentationActive() {
return rex::cvar::Query<bool>("skate3_ultrawide") &&
rex::graphics::ultrawide_debug::IsSkate3GameplayUltrawideActive() &&
rex::cvar::Query<double>("skate3_ultrawide_target_aspect") >
kSkate3BaseAspect + 0.01;
}
bool Skate3UltrawideConfiguredBeforeGameplay() {
return rex::cvar::Query<bool>("skate3_ultrawide") &&
!rex::graphics::ultrawide_debug::IsSkate3GameplayUltrawideActive() &&
rex::cvar::Query<double>("skate3_ultrawide_target_aspect") >
kSkate3BaseAspect + 0.01;
}
uint32_t GetConfiguredVideoModeWidth() { uint32_t GetConfiguredVideoModeWidth() {
int32_t configured_width = REXCVAR_GET(video_mode_width); int32_t configured_width = REXCVAR_GET(video_mode_width);
if (!rex::cvar::HasNonDefaultValue("video_mode_width")) { if (!rex::cvar::HasNonDefaultValue("video_mode_width") &&
!Skate3UltrawidePresentationActive() && !Skate3UltrawideConfiguredBeforeGameplay()) {
if (rex::cvar::HasNonDefaultValue("window_width") && REXCVAR_GET(window_width) > 0) { if (rex::cvar::HasNonDefaultValue("window_width") && REXCVAR_GET(window_width) > 0) {
configured_width = REXCVAR_GET(window_width); configured_width = REXCVAR_GET(window_width);
} else { } else {
@ -58,7 +75,8 @@ uint32_t GetConfiguredVideoModeWidth() {
uint32_t GetConfiguredVideoModeHeight() { uint32_t GetConfiguredVideoModeHeight() {
int32_t configured_height = REXCVAR_GET(video_mode_height); int32_t configured_height = REXCVAR_GET(video_mode_height);
if (!rex::cvar::HasNonDefaultValue("video_mode_height")) { if (!rex::cvar::HasNonDefaultValue("video_mode_height") &&
!Skate3UltrawidePresentationActive() && !Skate3UltrawideConfiguredBeforeGameplay()) {
if (rex::cvar::HasNonDefaultValue("window_height") && REXCVAR_GET(window_height) > 0) { if (rex::cvar::HasNonDefaultValue("window_height") && REXCVAR_GET(window_height) > 0) {
configured_height = REXCVAR_GET(window_height); configured_height = REXCVAR_GET(window_height);
} else { } else {

View file

@ -23,6 +23,7 @@
#include <rex/stream.h> #include <rex/stream.h>
#include <rex/system/function_dispatcher.h> #include <rex/system/function_dispatcher.h>
#include <rex/system/mmio_handler.h> #include <rex/system/mmio_handler.h>
#include <rex/system/thread_state.h>
#include <rex/system/xmemory.h> #include <rex/system/xmemory.h>
#include <rex/thread.h> #include <rex/thread.h>
@ -43,6 +44,12 @@ REXCVAR_DEFINE_BOOL(scribble_heap, false, "Memory", "Scribble 0xCD into all allo
namespace rex::memory { namespace rex::memory {
thread_local PhysicalMemoryInvalidationDiagnostics physical_memory_invalidation_diagnostics_;
const PhysicalMemoryInvalidationDiagnostics& GetPhysicalMemoryInvalidationDiagnostics() {
return physical_memory_invalidation_diagnostics_;
}
uint32_t get_page_count(uint32_t value, uint32_t page_size, uint32_t page_size_shift) { uint32_t get_page_count(uint32_t value, uint32_t page_size, uint32_t page_size_shift) {
return rex::round_up(value, page_size) >> page_size_shift; return rex::round_up(value, page_size) >> page_size_shift;
} }
@ -2024,6 +2031,23 @@ bool PhysicalHeap::TriggerCallbacks(std::unique_lock<std::recursive_mutex> globa
heap_size_ - (physical_address_start - physical_address_offset)); heap_size_ - (physical_address_start - physical_address_offset));
uint32_t unwatch_first = 0; uint32_t unwatch_first = 0;
uint32_t unwatch_last = UINT32_MAX; uint32_t unwatch_last = UINT32_MAX;
PhysicalMemoryInvalidationDiagnostics previous_diagnostics =
physical_memory_invalidation_diagnostics_;
physical_memory_invalidation_diagnostics_ = {};
physical_memory_invalidation_diagnostics_.valid = true;
physical_memory_invalidation_diagnostics_.virtual_address = virtual_address;
physical_memory_invalidation_diagnostics_.physical_address = physical_address_start;
physical_memory_invalidation_diagnostics_.length = physical_length;
physical_memory_invalidation_diagnostics_.exact_range = unwatch_exact_range;
physical_memory_invalidation_diagnostics_.unprotect = unprotect;
if (rex::runtime::ThreadState* thread_state = rex::runtime::ThreadState::Get()) {
physical_memory_invalidation_diagnostics_.thread_id = thread_state->thread_id();
if (PPCContext* ppc_context = thread_state->context()) {
physical_memory_invalidation_diagnostics_.lr = static_cast<uint32_t>(ppc_context->lr);
physical_memory_invalidation_diagnostics_.stack = ppc_context->r1.u32;
}
}
for (auto invalidation_callback : memory_->physical_memory_invalidation_callbacks_) { for (auto invalidation_callback : memory_->physical_memory_invalidation_callbacks_) {
std::pair<uint32_t, uint32_t> callback_unwatch_range = std::pair<uint32_t, uint32_t> callback_unwatch_range =
invalidation_callback->first(invalidation_callback->second, physical_address_start, invalidation_callback->first(invalidation_callback->second, physical_address_start,
@ -2035,6 +2059,7 @@ bool PhysicalHeap::TriggerCallbacks(std::unique_lock<std::recursive_mutex> globa
std::max(callback_unwatch_range.second, uint32_t(1)) - 1)); std::max(callback_unwatch_range.second, uint32_t(1)) - 1));
} }
} }
physical_memory_invalidation_diagnostics_ = previous_diagnostics;
if (!unwatch_exact_range) { if (!unwatch_exact_range) {
// Always unwatch at least the requested pages. // Always unwatch at least the requested pages.
unwatch_first = std::min(unwatch_first, physical_address_start); unwatch_first = std::min(unwatch_first, physical_address_start);

View file

@ -23,6 +23,7 @@ set(REXUI_OVERLAY_SOURCES
overlay/install_wizard_overlay.cpp overlay/install_wizard_overlay.cpp
overlay/simple_settings_overlay.cpp overlay/simple_settings_overlay.cpp
overlay/settings_overlay.cpp overlay/settings_overlay.cpp
overlay/ultrawide_targets_overlay.cpp
) )
# Vulkan UI backend # Vulkan UI backend

View file

@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cmath>
#include <string> #include <string>
#include <vector> #include <vector>
@ -18,7 +19,9 @@ namespace rex::ui {
namespace { namespace {
constexpr std::array<int32_t, 3> kResolutionScales = {1, 2, 3}; constexpr std::array<int32_t, 3> kResolutionScales = {1, 2, 3};
constexpr std::array<const char*, 3> kResolutionLabels = {"720p", "1440p", "2160p"}; constexpr std::array<const char*, 3> kResolutionLabels = {"720p (1x)", "1440p (2x)",
"2160p (3x)"};
constexpr std::array<const char*, 2> kAspectRatioLabels = {"16:9", "21:9 (Experimental)"};
constexpr std::array<double, 6> kFrameCapRates = {60.0, 90.0, 120.0, 144.0, 165.0, 240.0}; constexpr std::array<double, 6> kFrameCapRates = {60.0, 90.0, 120.0, 144.0, 165.0, 240.0};
constexpr std::array<const char*, 7> kFrameCapLabels = {"Unlimited", "60 FPS", "90 FPS", constexpr std::array<const char*, 7> kFrameCapLabels = {"Unlimited", "60 FPS", "90 FPS",
"120 FPS", "144 FPS", "165 FPS", "120 FPS", "144 FPS", "165 FPS",
@ -40,6 +43,12 @@ std::vector<std::string_view> GetSimpleSettingsCvars() {
std::vector<std::string_view> cvars; std::vector<std::string_view> cvars;
cvars.reserve(14); cvars.reserve(14);
cvars.insert(cvars.end(), kCoreSimpleSettingsCvars.begin(), kCoreSimpleSettingsCvars.end()); cvars.insert(cvars.end(), kCoreSimpleSettingsCvars.begin(), kCoreSimpleSettingsCvars.end());
if (HasCvar("skate3_ultrawide")) {
cvars.push_back("skate3_ultrawide");
}
if (HasCvar("skate3_field_of_view")) {
cvars.push_back("skate3_field_of_view");
}
if (HasCvar("d3d12_present_frame_limiter")) { if (HasCvar("d3d12_present_frame_limiter")) {
cvars.push_back("d3d12_present_frame_limiter"); cvars.push_back("d3d12_present_frame_limiter");
} }
@ -100,6 +109,17 @@ int FrameCapIndexFromCvar() {
return best; return best;
} }
bool HasFieldOfViewCvar() {
return HasCvar("skate3_field_of_view");
}
float FieldOfViewFromCvar() {
if (!HasFieldOfViewCvar()) {
return 60.0f;
}
return float(std::clamp(rex::cvar::Query<double>("skate3_field_of_view"), 40.0, 120.0));
}
void CopyToBuffer(char* buffer, size_t buffer_size, const std::string& value) { void CopyToBuffer(char* buffer, size_t buffer_size, const std::string& value) {
size_t count = std::min(value.size(), buffer_size - 1); size_t count = std::min(value.size(), buffer_size - 1);
std::copy_n(value.data(), count, buffer); std::copy_n(value.data(), count, buffer);
@ -215,6 +235,9 @@ void SimpleSettingsDialog::Show() {
void SimpleSettingsDialog::LoadSettingsFromCvars() { void SimpleSettingsDialog::LoadSettingsFromCvars() {
resolution_scale_index_ = ResolutionIndexFromCvar(); resolution_scale_index_ = ResolutionIndexFromCvar();
frame_cap_index_ = FrameCapIndexFromCvar(); frame_cap_index_ = FrameCapIndexFromCvar();
aspect_ratio_index_ =
HasCvar("skate3_ultrawide") && rex::cvar::Query<bool>("skate3_ultrawide") ? 1 : 0;
field_of_view_ = FieldOfViewFromCvar();
fullscreen_ = rex::cvar::Query<bool>("fullscreen"); fullscreen_ = rex::cvar::Query<bool>("fullscreen");
vsync_ = rex::cvar::Query<bool>("vsync"); vsync_ = rex::cvar::Query<bool>("vsync");
tearing_ = TearingFromCvar(); tearing_ = TearingFromCvar();
@ -225,6 +248,8 @@ void SimpleSettingsDialog::LoadSettingsFromCvars() {
bool SimpleSettingsDialog::HasSettingsChanges() const { bool SimpleSettingsDialog::HasSettingsChanges() const {
return resolution_scale_index_ != ResolutionIndexFromCvar() || return resolution_scale_index_ != ResolutionIndexFromCvar() ||
frame_cap_index_ != FrameCapIndexFromCvar() || frame_cap_index_ != FrameCapIndexFromCvar() ||
(HasCvar("skate3_ultrawide") &&
(aspect_ratio_index_ != 0) != rex::cvar::Query<bool>("skate3_ultrawide")) ||
fullscreen_ != rex::cvar::Query<bool>("fullscreen") || fullscreen_ != rex::cvar::Query<bool>("fullscreen") ||
vsync_ != rex::cvar::Query<bool>("vsync") || vsync_ != rex::cvar::Query<bool>("vsync") ||
tearing_ != TearingFromCvar() || tearing_ != TearingFromCvar() ||
@ -269,6 +294,9 @@ void SimpleSettingsDialog::SaveVideo() {
std::clamp(resolution_scale_index_, 0, static_cast<int>(kResolutionScales.size()) - 1); std::clamp(resolution_scale_index_, 0, static_cast<int>(kResolutionScales.size()) - 1);
frame_cap_index_ = frame_cap_index_ =
std::clamp(frame_cap_index_, 0, static_cast<int>(kFrameCapLabels.size()) - 1); std::clamp(frame_cap_index_, 0, static_cast<int>(kFrameCapLabels.size()) - 1);
aspect_ratio_index_ =
std::clamp(aspect_ratio_index_, 0, static_cast<int>(kAspectRatioLabels.size()) - 1);
field_of_view_ = std::clamp(field_of_view_, 40.0f, 120.0f);
const auto scale = std::to_string(kResolutionScales[resolution_scale_index_]); const auto scale = std::to_string(kResolutionScales[resolution_scale_index_]);
rex::cvar::SetFlagByName("resolution_scale", scale); rex::cvar::SetFlagByName("resolution_scale", scale);
rex::cvar::SetFlagByName("draw_resolution_scale_x", scale); rex::cvar::SetFlagByName("draw_resolution_scale_x", scale);
@ -281,6 +309,12 @@ void SimpleSettingsDialog::SaveVideo() {
std::to_string(kFrameCapRates[frame_cap_index_ - 1])); std::to_string(kFrameCapRates[frame_cap_index_ - 1]));
} }
SetBoolCvar("fullscreen", fullscreen_); SetBoolCvar("fullscreen", fullscreen_);
if (HasCvar("skate3_ultrawide")) {
SetBoolCvar("skate3_ultrawide", aspect_ratio_index_ != 0);
}
if (HasFieldOfViewCvar()) {
rex::cvar::SetFlagByName("skate3_field_of_view", std::to_string(field_of_view_));
}
SetBoolCvar("vsync", vsync_); SetBoolCvar("vsync", vsync_);
SetTearingCvars(tearing_); SetTearingCvars(tearing_);
SetBoolCvar("mnk_mode", mnk_mode_); SetBoolCvar("mnk_mode", mnk_mode_);
@ -396,7 +430,7 @@ void SimpleSettingsDialog::OnDraw(ImGuiIO& io) {
if (selected_tab_ == 0) { if (selected_tab_ == 0) {
SectionTitle("Video"); SectionTitle("Video");
BeginFieldRow("Resolution scale"); BeginFieldRow("Resolution Scale");
ImGui::Combo("##resolution_scale", &resolution_scale_index_, kResolutionLabels.data(), ImGui::Combo("##resolution_scale", &resolution_scale_index_, kResolutionLabels.data(),
static_cast<int>(kResolutionLabels.size())); static_cast<int>(kResolutionLabels.size()));
EndFieldRow(); EndFieldRow();
@ -408,6 +442,25 @@ void SimpleSettingsDialog::OnDraw(ImGuiIO& io) {
EndFieldRow(); EndFieldRow();
} }
if (HasCvar("skate3_ultrawide")) {
BeginFieldRow("Aspect Ratio");
ImGui::Combo("##aspect_ratio", &aspect_ratio_index_, kAspectRatioLabels.data(),
static_cast<int>(kAspectRatioLabels.size()));
EndFieldRow();
}
if (HasFieldOfViewCvar()) {
BeginFieldRow("Field of View");
if (ImGui::SliderFloat("##field_of_view", &field_of_view_, 40.0f, 120.0f, "%.0f")) {
field_of_view_ = std::clamp(field_of_view_, 40.0f, 120.0f);
rex::cvar::SetFlagByName("skate3_field_of_view", std::to_string(field_of_view_));
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
SaveSimpleSettingsConfig(config_path_);
}
EndFieldRow();
}
ImGui::Separator(); ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Checkbox("Fullscreen", &fullscreen_); ImGui::Checkbox("Fullscreen", &fullscreen_);

File diff suppressed because it is too large Load diff