VideoCommon: add logic to handle a GraphicsMod while Dolphin is running

This commit is contained in:
iwubcode 2022-03-05 00:41:14 -06:00
parent 254246b814
commit f416b71925
17 changed files with 729 additions and 0 deletions

View file

@ -637,6 +637,15 @@
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Constants.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModAction.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModGroup.h" />
<ClInclude Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.h" />
<ClInclude Include="VideoCommon\GXPipelineTypes.h" />
<ClInclude Include="VideoCommon\HiresTextures.h" />
<ClInclude Include="VideoCommon\ImageWrite.h" />
@ -1220,6 +1229,13 @@
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsModGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTarget.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Config\GraphicsTargetGroup.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\MoveAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\PrintAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\ScaleAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\Actions\SkipAction.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\FBInfo.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModActionFactory.cpp" />
<ClCompile Include="VideoCommon\GraphicsModSystem\Runtime\GraphicsModManager.cpp" />
<ClCompile Include="VideoCommon\HiresTextures_DDSLoader.cpp" />
<ClCompile Include="VideoCommon\HiresTextures.cpp" />
<ClCompile Include="VideoCommon\IndexGenerator.cpp" />

View file

@ -50,6 +50,21 @@ add_library(videocommon
GraphicsModSystem/Config/GraphicsTargetGroup.cpp
GraphicsModSystem/Config/GraphicsTargetGroup.h
GraphicsModSystem/Constants.h
GraphicsModSystem/Runtime/Actions/MoveAction.cpp
GraphicsModSystem/Runtime/Actions/MoveAction.h
GraphicsModSystem/Runtime/Actions/PrintAction.cpp
GraphicsModSystem/Runtime/Actions/PrintAction.h
GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
GraphicsModSystem/Runtime/Actions/ScaleAction.h
GraphicsModSystem/Runtime/Actions/SkipAction.cpp
GraphicsModSystem/Runtime/Actions/SkipAction.h
GraphicsModSystem/Runtime/FBInfo.cpp
GraphicsModSystem/Runtime/FBInfo.h
GraphicsModSystem/Runtime/GraphicsModAction.h
GraphicsModSystem/Runtime/GraphicsModActionFactory.cpp
GraphicsModSystem/Runtime/GraphicsModActionFactory.h
GraphicsModSystem/Runtime/GraphicsModManager.cpp
GraphicsModSystem/Runtime/GraphicsModManager.h
HiresTextures.cpp
HiresTextures.h
HiresTextures_DDSLoader.cpp

View file

@ -0,0 +1,47 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
std::unique_ptr<MoveAction> MoveAction::Create(const picojson::value& json_data)
{
Common::Vec3 position_offset;
const auto& x = json_data.get("X");
if (x.is<double>())
{
position_offset.x = static_cast<float>(x.get<double>());
}
const auto& y = json_data.get("Y");
if (y.is<double>())
{
position_offset.y = static_cast<float>(y.get<double>());
}
const auto& z = json_data.get("Z");
if (z.is<double>())
{
position_offset.z = static_cast<float>(z.get<double>());
}
return std::make_unique<MoveAction>(position_offset);
}
MoveAction::MoveAction(Common::Vec3 position_offset) : m_position_offset(position_offset)
{
}
void MoveAction::OnProjection(Common::Matrix44* matrix)
{
if (!matrix)
return;
*matrix *= Common::Matrix44::Translate(m_position_offset);
}
void MoveAction::OnProjectionAndTexture(Common::Matrix44* matrix)
{
if (!matrix)
return;
*matrix *= Common::Matrix44::Translate(m_position_offset);
}

View file

@ -0,0 +1,22 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <picojson.h>
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class MoveAction final : public GraphicsModAction
{
public:
static std::unique_ptr<MoveAction> Create(const picojson::value& json_data);
explicit MoveAction(Common::Vec3 position_offset);
void OnProjection(Common::Matrix44* matrix) override;
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
private:
Common::Vec3 m_position_offset;
};

View file

@ -0,0 +1,36 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
#include "Common/Logging/Log.h"
void PrintAction::OnDrawStarted(bool*)
{
INFO_LOG_FMT(VIDEO, "OnDrawStarted Called");
}
void PrintAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height)
{
if (!scaled_width || !scaled_height)
return;
INFO_LOG_FMT(VIDEO, "OnEFB Called. Original [{}, {}], Scaled [{}, {}]", texture_width,
texture_height, *scaled_width, *scaled_height);
}
void PrintAction::OnProjection(Common::Matrix44*)
{
INFO_LOG_FMT(VIDEO, "OnProjection Called");
}
void PrintAction::OnProjectionAndTexture(Common::Matrix44*)
{
INFO_LOG_FMT(VIDEO, "OnProjectionAndTexture Called");
}
void PrintAction::OnTextureLoad()
{
INFO_LOG_FMT(VIDEO, "OnTextureLoad Called");
}

View file

@ -0,0 +1,17 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class PrintAction final : public GraphicsModAction
{
public:
void OnDrawStarted(bool* skip) override;
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height) override;
void OnProjection(Common::Matrix44* matrix) override;
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
void OnTextureLoad() override;
};

View file

@ -0,0 +1,59 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h"
std::unique_ptr<ScaleAction> ScaleAction::Create(const picojson::value& json_data)
{
Common::Vec3 scale;
const auto& x = json_data.get("X");
if (x.is<double>())
{
scale.x = static_cast<float>(x.get<double>());
}
const auto& y = json_data.get("Y");
if (y.is<double>())
{
scale.y = static_cast<float>(y.get<double>());
}
const auto& z = json_data.get("Z");
if (z.is<double>())
{
scale.z = static_cast<float>(z.get<double>());
}
return std::make_unique<ScaleAction>(scale);
}
ScaleAction::ScaleAction(Common::Vec3 scale) : m_scale(scale)
{
}
void ScaleAction::OnEFB(bool*, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height)
{
if (scaled_width && m_scale.x > 0)
*scaled_width = texture_width * m_scale.x;
if (scaled_height && m_scale.y > 0)
*scaled_height = texture_height * m_scale.y;
}
void ScaleAction::OnProjection(Common::Matrix44* matrix)
{
if (!matrix)
return;
auto& the_matrix = *matrix;
the_matrix.data[0] = the_matrix.data[0] * m_scale.x;
the_matrix.data[5] = the_matrix.data[5] * m_scale.y;
}
void ScaleAction::OnProjectionAndTexture(Common::Matrix44* matrix)
{
if (!matrix)
return;
auto& the_matrix = *matrix;
the_matrix.data[0] = the_matrix.data[0] * m_scale.x;
the_matrix.data[5] = the_matrix.data[5] * m_scale.y;
}

View file

@ -0,0 +1,24 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <picojson.h>
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class ScaleAction final : public GraphicsModAction
{
public:
static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data);
explicit ScaleAction(Common::Vec3 scale);
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height) override;
void OnProjection(Common::Matrix44* matrix) override;
void OnProjectionAndTexture(Common::Matrix44* matrix) override;
private:
Common::Vec3 m_scale;
};

View file

@ -0,0 +1,20 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h"
void SkipAction::OnDrawStarted(bool* skip)
{
if (!skip)
return;
*skip = true;
}
void SkipAction::OnEFB(bool* skip, u32, u32, u32*, u32*)
{
if (!skip)
return;
*skip = true;
}

View file

@ -0,0 +1,14 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class SkipAction final : public GraphicsModAction
{
public:
void OnDrawStarted(bool* skip) override;
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height) override;
};

View file

@ -0,0 +1,22 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
#include "Common/Hash.h"
u32 FBInfo::CalculateHash() const
{
return Common::HashAdler32(reinterpret_cast<const u8*>(this), sizeof(FBInfo));
}
bool FBInfo::operator==(const FBInfo& other) const
{
return m_height == other.m_height && m_width == other.m_width &&
m_texture_format == other.m_texture_format;
}
bool FBInfo::operator!=(const FBInfo& other) const
{
return !(*this == other);
}

View file

@ -0,0 +1,25 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/CommonTypes.h"
#include "VideoCommon/TextureDecoder.h"
struct FBInfo
{
u32 m_height = 0;
u32 m_width = 0;
TextureFormat m_texture_format = TextureFormat::I4;
u32 CalculateHash() const;
bool operator==(const FBInfo& other) const;
bool operator!=(const FBInfo& other) const;
};
struct FBInfoHasher
{
std::size_t operator()(const FBInfo& fb_info) const noexcept
{
return static_cast<std::size_t>(fb_info.CalculateHash());
}
};

View file

@ -0,0 +1,29 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/CommonTypes.h"
#include "Common/Matrix.h"
class GraphicsModAction
{
public:
GraphicsModAction() = default;
virtual ~GraphicsModAction() = default;
GraphicsModAction(const GraphicsModAction&) = default;
GraphicsModAction(GraphicsModAction&&) = default;
GraphicsModAction& operator=(const GraphicsModAction&) = default;
GraphicsModAction& operator=(GraphicsModAction&&) = default;
virtual void OnDrawStarted(bool* skip) {}
virtual void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height)
{
}
virtual void OnXFB() {}
virtual void OnProjection(Common::Matrix44* matrix) {}
virtual void OnProjectionAndTexture(Common::Matrix44* matrix) {}
virtual void OnTextureLoad() {}
virtual void OnFrameEnd() {}
};

View file

@ -0,0 +1,34 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/MoveAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h"
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h"
namespace GraphicsModActionFactory
{
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data)
{
if (name == "print")
{
return std::make_unique<PrintAction>();
}
else if (name == "skip")
{
return std::make_unique<SkipAction>();
}
else if (name == "move")
{
return MoveAction::Create(json_data);
}
else if (name == "scale")
{
return ScaleAction::Create(json_data);
}
return nullptr;
}
} // namespace GraphicsModActionFactory

View file

@ -0,0 +1,16 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <string_view>
#include <picojson.h>
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
namespace GraphicsModActionFactory
{
std::unique_ptr<GraphicsModAction> Create(std::string_view name, const picojson::value& json_data);
}

View file

@ -0,0 +1,279 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
#include <string>
#include <string_view>
#include <variant>
#include "Common/Logging/Log.h"
#include "Common/VariantUtil.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/TextureInfo.h"
class GraphicsModManager::DecoratedAction final : public GraphicsModAction
{
public:
DecoratedAction(std::unique_ptr<GraphicsModAction> action, GraphicsModConfig mod)
: m_action_impl(std::move(action)), m_mod(std::move(mod))
{
}
void OnDrawStarted(bool* skip) override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnDrawStarted(skip);
}
void OnEFB(bool* skip, u32 texture_width, u32 texture_height, u32* scaled_width,
u32* scaled_height) override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnEFB(skip, texture_width, texture_height, scaled_width, scaled_height);
}
void OnProjection(Common::Matrix44* matrix) override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnProjection(matrix);
}
void OnProjectionAndTexture(Common::Matrix44* matrix) override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnProjectionAndTexture(matrix);
}
void OnTextureLoad() override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnTextureLoad();
}
void OnFrameEnd() override
{
if (!m_mod.m_enabled)
return;
m_action_impl->OnFrameEnd();
}
private:
GraphicsModConfig m_mod;
std::unique_ptr<GraphicsModAction> m_action_impl;
};
const std::vector<GraphicsModAction*>&
GraphicsModManager::GetProjectionActions(ProjectionType projection_type) const
{
if (const auto it = m_projection_target_to_actions.find(projection_type);
it != m_projection_target_to_actions.end())
{
return it->second;
}
return m_default;
}
const std::vector<GraphicsModAction*>&
GraphicsModManager::GetProjectionTextureActions(ProjectionType projection_type,
const std::string& texture_name) const
{
const auto lookup = fmt::format("{}_{}", texture_name, static_cast<int>(projection_type));
if (const auto it = m_projection_texture_target_to_actions.find(lookup);
it != m_projection_texture_target_to_actions.end())
{
return it->second;
}
return m_default;
}
const std::vector<GraphicsModAction*>&
GraphicsModManager::GetDrawStartedActions(const std::string& texture_name) const
{
if (const auto it = m_draw_started_target_to_actions.find(texture_name);
it != m_draw_started_target_to_actions.end())
{
return it->second;
}
return m_default;
}
const std::vector<GraphicsModAction*>&
GraphicsModManager::GetTextureLoadActions(const std::string& texture_name) const
{
if (const auto it = m_load_target_to_actions.find(texture_name);
it != m_load_target_to_actions.end())
{
return it->second;
}
return m_default;
}
const std::vector<GraphicsModAction*>& GraphicsModManager::GetEFBActions(const FBInfo& efb) const
{
if (const auto it = m_efb_target_to_actions.find(efb); it != m_efb_target_to_actions.end())
{
return it->second;
}
return m_default;
}
const std::vector<GraphicsModAction*>& GraphicsModManager::GetXFBActions(const FBInfo& xfb) const
{
if (const auto it = m_efb_target_to_actions.find(xfb); it != m_efb_target_to_actions.end())
{
return it->second;
}
return m_default;
}
void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
{
Reset();
const auto& mods = config.GetMods();
std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets;
for (const auto& mod : mods)
{
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
{
if (m_groups.find(group.m_name) != m_groups.end())
{
WARN_LOG_FMT(
VIDEO,
"Specified graphics mod group '{}' for mod '{}' is already specified by another mod.",
group.m_name, mod.m_title);
}
m_groups.insert(group.m_name);
const auto internal_group = fmt::format("{}.{}", mod.m_title, group.m_name);
for (const GraphicsTargetConfig& target : group.m_targets)
{
group_to_targets[group.m_name].push_back(target);
group_to_targets[internal_group].push_back(target);
}
}
}
for (const auto& mod : mods)
{
for (const GraphicsModFeatureConfig& feature : mod.m_features)
{
const auto create_action = [](const std::string_view& action_name,
const picojson::value& json_data,
GraphicsModConfig mod) -> std::unique_ptr<GraphicsModAction> {
auto action = GraphicsModActionFactory::Create(action_name, json_data);
if (action == nullptr)
{
return nullptr;
}
return std::make_unique<DecoratedAction>(std::move(action), std::move(mod));
};
const auto internal_group = fmt::format("{}.{}", mod.m_title, feature.m_group);
const auto add_target = [&](const GraphicsTargetConfig& target, GraphicsModConfig mod) {
auto action = create_action(feature.m_action, feature.m_action_data, std::move(mod));
if (action == nullptr)
{
WARN_LOG_FMT(VIDEO, "Failed to create action '{}' for group '{}'.", feature.m_action,
feature.m_group);
return;
}
m_actions.push_back(std::move(action));
std::visit(
overloaded{
[&](const DrawStartedTextureTarget& the_target) {
m_draw_started_target_to_actions[the_target.m_texture_info_string].push_back(
m_actions.back().get());
},
[&](const LoadTextureTarget& the_target) {
m_load_target_to_actions[the_target.m_texture_info_string].push_back(
m_actions.back().get());
},
[&](const EFBTarget& the_target) {
FBInfo info;
info.m_height = the_target.m_height;
info.m_width = the_target.m_width;
info.m_texture_format = the_target.m_texture_format;
m_efb_target_to_actions[info].push_back(m_actions.back().get());
},
[&](const XFBTarget& the_target) {
FBInfo info;
info.m_height = the_target.m_height;
info.m_width = the_target.m_width;
info.m_texture_format = the_target.m_texture_format;
m_xfb_target_to_actions[info].push_back(m_actions.back().get());
},
[&](const ProjectionTarget& the_target) {
if (the_target.m_texture_info_string)
{
const auto lookup = fmt::format("{}_{}", *the_target.m_texture_info_string,
static_cast<int>(the_target.m_projection_type));
m_projection_texture_target_to_actions[lookup].push_back(
m_actions.back().get());
}
else
{
m_projection_target_to_actions[the_target.m_projection_type].push_back(
m_actions.back().get());
}
},
},
target);
};
// Prefer groups in the pack over groups from another pack
if (const auto local_it = group_to_targets.find(internal_group);
local_it != group_to_targets.end())
{
for (const GraphicsTargetConfig& target : local_it->second)
{
add_target(target, mod);
}
}
else if (const auto global_it = group_to_targets.find(feature.m_group);
global_it != group_to_targets.end())
{
for (const GraphicsTargetConfig& target : global_it->second)
{
add_target(target, mod);
}
}
else
{
WARN_LOG_FMT(VIDEO, "Specified graphics mod group '{}' was not found for mod '{}'",
feature.m_group, mod.m_title);
}
}
}
}
void GraphicsModManager::EndOfFrame()
{
for (auto&& action : m_actions)
{
action->OnFrameEnd();
}
}
void GraphicsModManager::Reset()
{
m_actions.clear();
m_groups.clear();
m_projection_target_to_actions.clear();
m_projection_texture_target_to_actions.clear();
m_draw_started_target_to_actions.clear();
m_load_target_to_actions.clear();
m_efb_target_to_actions.clear();
m_xfb_target_to_actions.clear();
}

View file

@ -0,0 +1,54 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
#include "VideoCommon/TextureInfo.h"
#include "VideoCommon/XFMemory.h"
class GraphicsModGroupConfig;
class GraphicsModManager
{
public:
const std::vector<GraphicsModAction*>& GetProjectionActions(ProjectionType projection_type) const;
const std::vector<GraphicsModAction*>&
GetProjectionTextureActions(ProjectionType projection_type,
const std::string& texture_name) const;
const std::vector<GraphicsModAction*>&
GetDrawStartedActions(const std::string& texture_name) const;
const std::vector<GraphicsModAction*>&
GetTextureLoadActions(const std::string& texture_name) const;
const std::vector<GraphicsModAction*>& GetEFBActions(const FBInfo& efb) const;
const std::vector<GraphicsModAction*>& GetXFBActions(const FBInfo& xfb) const;
void Load(const GraphicsModGroupConfig& config);
void EndOfFrame();
private:
void Reset();
class DecoratedAction;
static inline const std::vector<GraphicsModAction*> m_default = {};
std::list<std::unique_ptr<GraphicsModAction>> m_actions;
std::unordered_map<ProjectionType, std::vector<GraphicsModAction*>>
m_projection_target_to_actions;
std::unordered_map<std::string, std::vector<GraphicsModAction*>>
m_projection_texture_target_to_actions;
std::unordered_map<std::string, std::vector<GraphicsModAction*>> m_draw_started_target_to_actions;
std::unordered_map<std::string, std::vector<GraphicsModAction*>> m_load_target_to_actions;
std::unordered_map<FBInfo, std::vector<GraphicsModAction*>, FBInfoHasher> m_efb_target_to_actions;
std::unordered_map<FBInfo, std::vector<GraphicsModAction*>, FBInfoHasher> m_xfb_target_to_actions;
std::unordered_set<std::string> m_groups;
};