mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-19 12:01:49 +02:00
331 lines
13 KiB
C++
331 lines
13 KiB
C++
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "VideoCommon/MetroidElementClassifier.h"
|
|
#include "VideoCommon/ShaderHunter.h"
|
|
|
|
class ElementsGroupManager
|
|
{
|
|
public:
|
|
using RuntimeElementSignature = ShaderHunter::RuntimeElementSignature;
|
|
using ShaderType = ShaderHunter::ShaderType;
|
|
using HandlingType = ShaderHunter::HandlingType;
|
|
using HuntingOption = ShaderHunter::HuntingOption;
|
|
using CameraAnchorParams = ShaderHunter::CameraAnchorParams;
|
|
using ControllerAnchorParams = ShaderHunter::ControllerAnchorParams;
|
|
|
|
enum class PreviewAction
|
|
{
|
|
None = 0,
|
|
Skip = 1,
|
|
Pink = 2
|
|
};
|
|
|
|
enum class MatchKind
|
|
{
|
|
RuntimeSignature = 0,
|
|
ProfileLayer = 1,
|
|
};
|
|
|
|
enum class ScreenPaneDepthMode
|
|
{
|
|
Game = 0,
|
|
VR = 1,
|
|
Flat = 2,
|
|
};
|
|
|
|
struct DrawRecord
|
|
{
|
|
int draw_index = -1;
|
|
u32 draw_sequence = 0;
|
|
u64 vs_hash = 0;
|
|
u64 ps_hash = 0;
|
|
u64 gs_hash = 0;
|
|
u64 vs_family = 0;
|
|
u64 ps_family = 0;
|
|
u64 gs_family = 0;
|
|
RuntimeElementSignature signature;
|
|
MetroidElementProfile profile_id = MetroidElementProfile::None;
|
|
MetroidElementLayer profile_layer = MetroidElementLayer::Unknown;
|
|
std::string profile_layer_name;
|
|
std::array<u64, 8> textures{};
|
|
std::array<std::string, 8> texture_names{};
|
|
|
|
u64 GetHash(ShaderType type) const;
|
|
u64 GetFamily(ShaderType type) const;
|
|
};
|
|
|
|
struct StableSubMatchSignature
|
|
{
|
|
RuntimeElementSignature runtime_element;
|
|
u64 vs_family = 0;
|
|
u64 ps_family = 0;
|
|
u64 gs_family = 0;
|
|
std::vector<u64> texture_hashes;
|
|
int occurrence_slot = -1;
|
|
};
|
|
|
|
struct SelectedSubgroupSignature
|
|
{
|
|
u64 vs_family = 0;
|
|
u64 ps_family = 0;
|
|
u64 gs_family = 0;
|
|
std::vector<u64> texture_hashes;
|
|
// Scheme of the family values (see ShaderHunter::FAMILY_SCHEME_VERSION). Filters from pre-v6
|
|
// INIs are version 1: their vs/gs families are raw-UID CRC32s (equal to the exact shader
|
|
// hashes) and are matched against draw hashes for backward compatibility. Not part of
|
|
// signature equality.
|
|
u32 family_version = ShaderHunter::FAMILY_SCHEME_VERSION;
|
|
};
|
|
|
|
struct CurrentMatchCandidate
|
|
{
|
|
SelectedSubgroupSignature subgroup;
|
|
DrawRecord representative_draw;
|
|
int raw_draw_count = 0;
|
|
bool active_this_frame = false;
|
|
};
|
|
|
|
struct ElementGroupOverride
|
|
{
|
|
std::string name;
|
|
std::string comments;
|
|
std::string credits;
|
|
MatchKind match_kind = MatchKind::RuntimeSignature;
|
|
HandlingType handling = HandlingType::Skip;
|
|
// Fullscreen handling: preserve separate eye layers when a one-layer texture was partially
|
|
// updated from a stereo EFB copy. Opt-in because most fullscreen effects are intentionally
|
|
// mono and do not need the extra texture allocation/copies.
|
|
bool preserve_stereo_efb = false;
|
|
RuntimeElementSignature runtime_element;
|
|
MetroidElementProfile profile_id = MetroidElementProfile::None;
|
|
std::vector<MetroidElementLayer> profile_layers;
|
|
float element_depth = -1.0f;
|
|
float units_per_meter = -1.0f;
|
|
// ScreenPane handling: preserve the game's depth buffer values by default, or use the
|
|
// physically projected VR depth of the re-anchored pane geometry with shared Z composition.
|
|
ScreenPaneDepthMode screen_pane_depth = ScreenPaneDepthMode::Game;
|
|
float passthrough_opacity = 0.0f; // Passthrough handling: element opacity (0 = fully camera)
|
|
// CameraAnchor handling: camera-space offset from the anchor element's origin (meters,
|
|
// right/up/forward, converted with units-per-meter and mapped to view-space axes at capture
|
|
// time) and whether to hide the anchor element itself (e.g. the head mesh in first person).
|
|
float anchor_right = 0.0f;
|
|
float anchor_up = 0.0f;
|
|
float anchor_forward = 0.0f;
|
|
bool anchor_hide = true;
|
|
// How much of the element's orientation the camera follows, and a fixed yaw correction in
|
|
// degrees for models whose forward axis is not the assumed object +Z (e.g. 180 = view was
|
|
// facing backward).
|
|
ShaderHunter::AnchorRotationMode anchor_rotation = ShaderHunter::AnchorRotationMode::Off;
|
|
float anchor_yaw_deg = 0.0f;
|
|
// World scale to use while this anchor is active, so a first-person view can have its own
|
|
// scale without disturbing the game's global Units per Meter (-1 = keep the global value).
|
|
float anchor_units_per_meter = -1.0f;
|
|
// ControllerAnchor handling: which VR controller the element follows (0 = left, 1 = right).
|
|
// Reuses anchor_right/up/forward as meter offsets from the controller's aim pose and
|
|
// anchor_rotation (Off/Full) for orientation follow; yaw/pitch/roll are the model-axis
|
|
// correction in the controller's frame (models differ in which way they natively point).
|
|
int anchor_hand = 1;
|
|
float anchor_pitch_deg = 0.0f;
|
|
float anchor_roll_deg = 0.0f;
|
|
std::vector<u64> texture_hashes;
|
|
bool texture_hashes_excluded = false;
|
|
std::vector<SelectedSubgroupSignature> selected_match_filter;
|
|
bool selected_match_filter_excluded = false;
|
|
bool enabled = true;
|
|
bool user_defined = true;
|
|
std::string flag_group;
|
|
std::string condition_flag;
|
|
bool condition_inverted = false;
|
|
// Clear EFB: when a draw matching this override is rendered, clear the next EFB-to-texture copy
|
|
// to transparent instead of copying. Independent of handling (can combine with Skip/Screen/etc.).
|
|
// Because matching is on the full element signature, only this element arms the clear.
|
|
bool clear_efb = false;
|
|
int clear_efb_min_width = 0; // Only clear copies with native width >= this (0 = any).
|
|
int clear_efb_max_width = 0; // Only clear copies with native width <= this (0 = any).
|
|
};
|
|
|
|
struct SeedCandidate
|
|
{
|
|
RuntimeElementSignature signature;
|
|
RuntimeElementSignature group_signature;
|
|
DrawRecord representative_draw;
|
|
int occurrence_count = 0;
|
|
};
|
|
|
|
struct Status
|
|
{
|
|
bool popup_open = false;
|
|
bool hunt_enabled = false;
|
|
HuntingOption option = HuntingOption::Skip;
|
|
bool seed_valid = false;
|
|
RuntimeElementSignature seed_signature;
|
|
std::optional<DrawRecord> seed_draw;
|
|
int selected_seed_index = -1;
|
|
int total_seed_candidates = 0;
|
|
int selected_match = 0;
|
|
int total_matches = 0;
|
|
int active_matches = 0;
|
|
int selected_match_filter_count = 0;
|
|
bool selected_match_filter_excluded = false;
|
|
int highlighted_match_raw_draw_count = 0;
|
|
std::optional<DrawRecord> highlighted_draw;
|
|
};
|
|
|
|
static ElementsGroupManager& GetInstance();
|
|
|
|
static std::vector<ElementGroupOverride> LoadOverridesFromINI(
|
|
const std::string& game_id, std::optional<u16> revision = std::nullopt);
|
|
static void SaveOverridesToINI(const std::string& game_id,
|
|
const std::vector<ElementGroupOverride>& overrides);
|
|
static RuntimeElementSignature MakeSelectedMatchFilterSignature(
|
|
const RuntimeElementSignature& signature);
|
|
static StableSubMatchSignature MakeStableSubMatchSignature(const DrawRecord& draw,
|
|
int occurrence_slot);
|
|
static SelectedSubgroupSignature MakeSelectedSubgroupSignature(const DrawRecord& draw);
|
|
|
|
void LoadOverrides(const std::string& game_id);
|
|
void LoadOverridesIfNeeded(const std::string& game_id);
|
|
bool HasOverrides() const;
|
|
|
|
void SetPopupOpen(bool open);
|
|
bool IsPopupOpen() const;
|
|
void SetHuntEnabled(bool enabled);
|
|
bool IsHuntEnabled() const;
|
|
void SetHuntingOption(HuntingOption option);
|
|
HuntingOption GetHuntingOption() const;
|
|
|
|
void SelectSeedCandidate(int index);
|
|
void SetSeedGroupMask(bool projection, bool layer, bool viewport, bool scissor,
|
|
bool render_state);
|
|
void SelectMatch(int index);
|
|
void NextMatch();
|
|
void PrevMatch();
|
|
|
|
std::vector<SeedCandidate> GetSeedCandidates() const;
|
|
std::vector<CurrentMatchCandidate> GetCurrentMatches() const;
|
|
std::vector<SelectedSubgroupSignature> GetSelectedMatchFilters() const;
|
|
bool GetSelectedMatchFilterExcluded() const;
|
|
std::vector<CurrentMatchCandidate> GetSelectedMatchDisplayDraws() const;
|
|
std::vector<DrawRecord> GetCurrentTextureSourceDraws() const;
|
|
std::optional<DrawRecord> GetHighlightedDraw() const;
|
|
std::optional<DrawRecord> GetSelectedSeedDraw() const;
|
|
std::optional<DrawRecord> GetSelectedCurrentMatchDraw() const;
|
|
std::optional<DrawRecord> GetCurrentTextureSourceDraw() const;
|
|
RuntimeElementSignature GetSeedSignature() const;
|
|
Status GetStatus() const;
|
|
bool IsCurrentMatchFilterEnabled(int match_index) const;
|
|
void SetSelectedMatchFilterExcluded(bool excluded);
|
|
void SetCurrentMatchFilterEnabled(int match_index, bool enabled);
|
|
void AddCurrentMatchFilter();
|
|
void RemoveSelectedMatchFilter(int index);
|
|
|
|
PreviewAction RegisterDraw(const DrawRecord& draw);
|
|
void OnFrameEnd();
|
|
|
|
void RegisterFlagsForDraw(const DrawRecord& draw);
|
|
bool ShouldSkipByOverride(const DrawRecord& draw) const;
|
|
HandlingType GetOverrideHandling(const DrawRecord& draw,
|
|
bool* out_preserve_stereo_efb = nullptr) const;
|
|
ScreenPaneDepthMode GetOverrideScreenPaneDepth(const DrawRecord& draw,
|
|
u64* out_group_id = nullptr) const;
|
|
float GetOverrideElementDepth(const DrawRecord& draw) const;
|
|
float GetOverrideUnitsPerMeter(const DrawRecord& draw) const;
|
|
// Returns the opacity for a Passthrough override (0 = fully see-through to the camera).
|
|
float GetOverridePassthroughOpacity(const DrawRecord& draw) const;
|
|
// Fills the parameters of the first matching CameraAnchor override.
|
|
// Returns false when none matches the draw.
|
|
bool GetOverrideCameraAnchor(const DrawRecord& draw, CameraAnchorParams* out_params) const;
|
|
// Fills the parameters of the first matching ControllerAnchor override.
|
|
// Returns false when none matches the draw.
|
|
bool GetOverrideControllerAnchor(const DrawRecord& draw, ControllerAnchorParams* out_params) const;
|
|
// Clear EFB: arm a pending clear when a draw matches a clear_efb override; ShouldClearEFBCopy is
|
|
// consumed by the next EFB-to-texture copy (see TextureCacheBase).
|
|
void CheckClearEFBForDraw(const DrawRecord& draw);
|
|
bool ShouldClearEFBCopy(int width);
|
|
|
|
private:
|
|
ElementsGroupManager() = default;
|
|
|
|
struct StableSubMatchContext
|
|
{
|
|
bool valid = false;
|
|
u32 draw_sequence = 0;
|
|
StableSubMatchSignature signature;
|
|
};
|
|
|
|
struct GroupMask
|
|
{
|
|
bool projection = false;
|
|
bool layer = false;
|
|
bool viewport = false;
|
|
bool scissor = false;
|
|
bool render_state = false;
|
|
};
|
|
|
|
void ClearSeedSelectionLocked();
|
|
void SelectSeedCandidateLocked(int index);
|
|
bool HasActiveHuntLocked() const;
|
|
RuntimeElementSignature GetMaskedSeedSignatureLocked() const;
|
|
RuntimeElementSignature GetSeedGroupSignatureLocked(const RuntimeElementSignature& signature) const;
|
|
bool MatchesSelectedMatchFilterLocked(const DrawRecord& draw) const;
|
|
bool MatchesSelectedMatchFilterSignatureLocked(const SelectedSubgroupSignature& signature) const;
|
|
std::vector<CurrentMatchCandidate> ResolveSelectedMatchDisplayDrawsLocked() const;
|
|
StableSubMatchSignature GetStableSubMatchSignatureLocked(const DrawRecord& draw) const;
|
|
bool DoesEntryBaseMatch(const ElementGroupOverride& entry, const DrawRecord& draw) const;
|
|
bool DoesSelectedMatchFilterPass(const ElementGroupOverride& entry, const DrawRecord& draw) const;
|
|
bool DoesEntryMatch(const ElementGroupOverride& entry, const DrawRecord& draw,
|
|
bool include_condition) const;
|
|
bool DoesTextureFilterPass(const DrawRecord& draw, const ElementGroupOverride& entry) const;
|
|
|
|
mutable std::mutex m_mutex;
|
|
int m_popup_open_count = 0;
|
|
std::atomic_bool m_popup_open = false;
|
|
std::atomic_bool m_has_overrides = false;
|
|
bool m_hunt_enabled = false;
|
|
HuntingOption m_hunting_option = HuntingOption::Skip;
|
|
RuntimeElementSignature m_seed_signature{};
|
|
RuntimeElementSignature m_seed_group_signature{};
|
|
std::optional<DrawRecord> m_seed_draw;
|
|
GroupMask m_group_mask;
|
|
int m_selected_seed_index = -1;
|
|
int m_selected_match = 0;
|
|
std::vector<SelectedSubgroupSignature> m_selected_match_filters;
|
|
bool m_selected_match_filters_excluded = false;
|
|
|
|
std::vector<DrawRecord> m_collecting_draws;
|
|
std::vector<DrawRecord> m_display_draws;
|
|
std::vector<SeedCandidate> m_display_seed_candidates;
|
|
std::vector<DrawRecord> m_collecting_matches;
|
|
std::vector<DrawRecord> m_display_raw_matches;
|
|
std::vector<SelectedSubgroupSignature> m_display_raw_match_signatures;
|
|
std::vector<CurrentMatchCandidate> m_display_matches;
|
|
int m_collecting_match_total = 0;
|
|
int m_display_match_total = 0;
|
|
std::optional<DrawRecord> m_collecting_highlighted_draw;
|
|
std::optional<DrawRecord> m_display_highlighted_draw;
|
|
int m_display_highlighted_match_raw_draw_count = 0;
|
|
|
|
std::vector<ElementGroupOverride> m_overrides;
|
|
std::string m_loaded_game_id;
|
|
|
|
mutable std::unordered_map<u64, int> m_stable_submatch_occurrence_counters;
|
|
mutable StableSubMatchContext m_current_stable_submatch;
|
|
|
|
// Clear EFB pending state (video thread): armed by CheckClearEFBForDraw, consumed by the next
|
|
// ShouldClearEFBCopy.
|
|
bool m_clear_next_efb = false;
|
|
int m_pending_clear_min = 0;
|
|
int m_pending_clear_max = 0;
|
|
};
|