#pragma once #include "util/helpers/helpers.h" #include "Cemu/ExpressionParser/ExpressionParser.h" #include "Cafe/HW/Latte/Renderer/RendererOuputShader.h" #include "util/helpers/Serializer.h" #include "Cafe/OS/RPL/rpl.h" #include "Cemu/PPCAssembler/ppcAssembler.h" #include #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "GraphicPack2Patches.h" #include "util/IniParser/IniParser.h" class GraphicPack2 { public: enum class GP_SHADER_TYPE : uint8 { PIXEL = 0, VERTEX = 1, GEOMETRY = 2, }; enum { GFXPACK_VERSION_5 = 5, GFXPACK_VERSION_6 = 6, // added memory extensions GFXPACK_VERSION_7 = 7, // added fine-grained origin control in patch format (no more forced 4 byte alignment), .string directive (an alias to .byte) and support for more than one constant per data directive }; struct TextureRule { // filter (texture must match these settings) struct FILTER_SETTINGS { enum class MEM1_FILTER { BOTH, INSIDE, OUTSIDE, }; sint32 width = -1; sint32 height = -1; sint32 depth = -1; MEM1_FILTER inMEM1 = MEM1_FILTER::BOTH; std::vector format_whitelist{}; std::vector format_blacklist{}; std::vector tilemode_whitelist{}; std::vector tilemode_blacklist{}; } filter_settings; // overwrite (if match found, these settings are overwritten) struct OVERWRITE_SETTINGS { sint32 width = -1; sint32 height = -1; sint32 depth = -1; sint32 format = -1; sint32 lod_bias = -1; // in 1/64th steps sint32 relative_lod_bias = -1; // in 1/64th steps sint32 anistropic_value = -1; // 1<; struct Preset { std::string category; // preset category (empty for default) std::string name; // displayed name std::string condition; std::unordered_map variables; bool active = false; // selected/active preset bool visible = true; // set by condition or true bool is_default = false; // selected by default Preset(std::string_view name, std::unordered_map vars) : name(name), variables(std::move(vars)) {} Preset(std::string_view category, std::string_view name, std::unordered_map vars) : category(category), name(name), variables(std::move(vars)) {} Preset(std::string_view category, std::string_view name, std::string_view condition, std::unordered_map vars) : category(category), name(name), condition(condition), variables(std::move(vars)) {} }; using PresetPtr = std::shared_ptr; GraphicPack2(fs::path rulesPath, IniParser& rules); bool IsEnabled() const { return m_enabled; } bool IsActivated() const { return m_activated; } sint32 GetVersion() const { return m_version; } const fs::path GetRulesPath() const { return m_rulesPath; } std::string GetNormalizedPathString() const; bool RequiresRestart(bool changeEnableState, bool changePreset); bool Reload(); bool HasName() const { return !m_name.empty(); } const std::string& GetName() const { return m_name.empty() ? m_virtualPath : m_name; } const std::string& GetVirtualPath() const { return m_virtualPath; } // returns the path in the gfx tree hierarchy const std::string& GetDescription() const { return m_description; } bool IsDefaultEnabled() const { return m_default_enabled; } bool AllowRendertargetSizeOptimization() const { return m_allowRendertargetSizeOptimization; } void SetEnabled(bool state) { m_enabled = state; } bool ContainsTitleId(uint64_t title_id) const; const std::vector& GetTitleIds() const { return m_title_ids; } bool HasCustomVSyncFrequency() const { return m_vsync_frequency >= 1; } sint32 GetCustomVSyncFrequency() const { return m_vsync_frequency; } // texture rules const std::vector& GetTextureRules() const { return m_texture_rules; } // presets [[nodiscard]] bool HasActivePreset() const; [[nodiscard]] std::string GetActivePreset(std::string_view category = "") const; [[nodiscard]] std::vector GetActivePresets() const; [[nodiscard]] bool IsPresetVisible(const PresetPtr& preset) const; [[nodiscard]] std::optional GetPresetVariable(const std::vector& presets, std::string_view var_name) const; void ValidatePresetSelections(); bool SetActivePreset(std::string_view category, std::string_view name, bool update_visibility = true); bool SetActivePreset(std::string_view name); void UpdatePresetVisibility(); void AddConstantsForCurrentPreset(ExpressionParser& ep); bool ResolvePresetConstant(const std::string& varname, double& value) const; [[nodiscard]] const std::vector& GetPresets() const { return m_presets; } [[nodiscard]] std::unordered_map> GetCategorizedPresets(std::vector& order) const; // shaders void LoadShaders(); bool HasShaders() const; const std::vector& GetCustomShaders() const { return m_custom_shaders; } static const std::string* FindCustomShaderSource(uint64 shaderBaseHash, uint64 shaderAuxHash, GP_SHADER_TYPE type, bool isVulkanRenderer); const std::string& GetOutputShaderSource() const { return m_output_shader_source; } const std::string& GetDownscalingShaderSource() const { return m_downscaling_shader_source; } const std::string& GetUpscalingShaderSource() const { return m_upscaling_shader_source; } RendererOutputShader* GetOuputShader(bool render_upside_down); RendererOutputShader* GetUpscalingShader(bool render_upside_down); RendererOutputShader* GetDownscalingShader(bool render_upside_down); LatteTextureView::MagFilter GetUpscalingMagFilter() const { return m_output_settings.upscale_filter; } LatteTextureView::MagFilter GetDownscalingMagFilter() const { return m_output_settings.downscale_filter; } // static methods static void LoadAll(); static const std::vector>& GetGraphicPacks() { return s_graphic_packs; } static const std::vector>& GetActiveGraphicPacks() { return s_active_graphic_packs; } static void LoadGraphicPack(fs::path graphicPackPath); static bool LoadGraphicPack(const fs::path& rulesPath, class IniParser& rules); static bool ActivateGraphicPack(const std::shared_ptr& graphic_pack); static bool DeactivateGraphicPack(const std::shared_ptr& graphic_pack); static void ClearGraphicPacks(); static void WaitUntilReady(); // wait until all graphic packs finished activation static void ActivateForCurrentTitle(); static void Reset(); private: bool Activate(); bool Deactivate(); static std::vector> s_graphic_packs; static std::vector> s_active_graphic_packs; static std::atomic_bool s_isReady; template void FillPresetConstants(TExpressionParser& parser) const { // fils preset variables with priority // active && visible > active > default const auto active_presets = GetActivePresets(); for(const auto& preset : active_presets) { if(preset->visible) { for (auto& var : preset->variables) parser.AddConstant(var.first, (TType)var.second.second); } } for(const auto& preset : active_presets) { if(!preset->visible) { for (auto& var : preset->variables) parser.TryAddConstant(var.first, (TType)var.second.second); } } for (auto& var : m_preset_vars) parser.TryAddConstant(var.first, (TType)var.second.second); } fs::path m_rulesPath; sint32 m_version; std::string m_name; std::string m_virtualPath; std::string m_description; bool m_default_enabled = false; bool m_allowRendertargetSizeOptimization = false; // gfx pack supports framebuffers with non-padded sizes, which is an optional optimization introduced with Cemu 2.0-74 // filter std::optional m_renderer_api; std::optional m_gfx_vendor; bool m_enabled = false; bool m_activated = false; // set if the graphic pack is currently used by the running game std::vector m_title_ids; bool m_patchedFilesLoaded = false; // set to true once patched files are loaded sint32 m_vsync_frequency = -1; sint32 m_fs_priority = 100; struct { LatteTextureView::MagFilter upscale_filter = LatteTextureView::MagFilter::kLinear; LatteTextureView::MagFilter downscale_filter = LatteTextureView::MagFilter::kLinear; } m_output_settings; std::vector m_presets; // default preset vars std::unordered_map m_preset_vars; std::vector m_custom_shaders; std::vector m_texture_rules; std::string m_output_shader_source, m_upscaling_shader_source, m_downscaling_shader_source; std::unique_ptr m_output_shader, m_upscaling_shader, m_downscaling_shader, m_output_shader_ud, m_upscaling_shader_ud, m_downscaling_shader_ud; template bool ParseRule(const ExpressionParser& parser, IniParser& iniParser, const char* option_name, T* value_out) const; template std::vector ParseList(const ExpressionParser& parser, IniParser& iniParser, const char* option_name) const; std::unordered_map ParsePresetVars(IniParser& rules) const; std::vector ParseTitleIds(IniParser& rules, const char* option_name) const; CustomShader LoadShader(const fs::path& path, uint64 shader_base_hash, uint64 shader_aux_hash, GP_SHADER_TYPE shader_type) const; void ApplyShaderPresets(std::string& shader_source) const; void LoadReplacedFiles(); void _iterateReplacedFiles(const fs::path& currentPath, bool isAOC); // ram mappings std::vector> m_ramMappings; // patches void LoadPatchFiles(); // loads Cemuhook or Cemu patches bool LoadCemuPatches(); void ParseCemuhookPatchesTxtInternal(MemStreamReader& patchesStream); bool ParseCemuPatchesTxtInternal(MemStreamReader& patchesStream); void CancelParsingPatches(); void ApplyPatchGroups(std::vector& groups, const RPLModule* rpl); void UndoPatchGroups(std::vector& groups, const RPLModule* rpl); void AddPatchGroup(PatchGroup* group); sint32 GetLengthWithoutComment(const char* str, size_t length); void LogPatchesSyntaxError(sint32 lineNumber, std::string_view errorMsg); std::vector list_patchGroups; static std::recursive_mutex mtx_patches; static std::vector list_modules; public: static std::vector> GetActiveRAMMappings(); void EnablePatches(); void UnloadPatches(); bool HasPatches(); const std::vector& GetPatchGroups(); void ApplyPatchesForModule(const RPLModule* rpl); void RevertPatchesForModule(const RPLModule* rpl); static void NotifyModuleLoaded(const RPLModule* rpl); static void NotifyModuleUnloaded(const RPLModule* rpl); }; using GraphicPackPtr = std::shared_ptr; template bool GraphicPack2::ParseRule(const ExpressionParser& parser, IniParser& iniParser, const char* option_name, T* value_out) const { auto option_value = iniParser.FindOption(option_name); if (option_value) { *value_out = parser.Evaluate(*option_value); return true; } return false; } template std::vector GraphicPack2::ParseList(const ExpressionParser& parser, IniParser& iniParser, const char* option_name) const { std::vector result; auto option_text = iniParser.FindOption(option_name); if (!option_text) return result; for(auto& token : Tokenize(*option_text, ',')) { try { result.emplace_back(parser.Evaluate(token)); } catch (const std::invalid_argument&) {} } return result; }