summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2026-04-03 13:38:04 -0400
committerGitHub <noreply@github.com>2026-04-03 13:38:04 -0400
commit798f581c38acfbd9196ba4884cfb9dfd6ef85684 (patch)
tree3421446055c78b54837f5d51372c3611fc65eebb /Source/Core/VideoCommon
parentd3b89b4c39a312b52546c3604379b3f51ec3c876 (diff)
parent306daff51728d71efa833dfaa5b32b3693178874 (diff)
Merge pull request #14140 from iwubcode/blurry_bloom
VideoCommon: add a graphics mod feature to modify EFBs with a custom material, enhance bloom
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.cpp22
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp78
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h13
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.cpp2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.cpp2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.cpp2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h2
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h5
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h8
-rw-r--r--Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp14
-rw-r--r--Source/Core/VideoCommon/Resources/CustomResourceManager.cpp30
-rw-r--r--Source/Core/VideoCommon/Resources/CustomResourceManager.h19
-rw-r--r--Source/Core/VideoCommon/Resources/MaterialResource.cpp105
-rw-r--r--Source/Core/VideoCommon/Resources/MaterialResource.h6
-rw-r--r--Source/Core/VideoCommon/Resources/ShaderResource.cpp312
-rw-r--r--Source/Core/VideoCommon/Resources/ShaderResource.h9
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.cpp130
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.h3
20 files changed, 605 insertions, 161 deletions
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.cpp
index e3cf5b9a10..c0fa1f82ca 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.cpp
@@ -10,6 +10,7 @@
#include <picojson.h>
#include "Common/CommonPaths.h"
+#include "Common/FileSearch.h"
#include "Common/FileUtil.h"
#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h"
@@ -84,13 +85,22 @@ void GraphicsModGroupConfig::Load()
const auto try_add_mod = [&known_paths, this](const std::string& dir,
GraphicsModConfig::Source source) {
- auto file = dir + DIR_SEP + "metadata.json";
- UnifyPathSeparators(file);
- if (known_paths.contains(file))
- return;
+ const auto files = Common::DoFileSearch(dir, ".json", true);
+ for (const auto& file : files)
+ {
+ std::string basename;
+ SplitPath(file, nullptr, &basename, nullptr);
+ if (basename == "metadata")
+ {
+ auto file_copy = file;
+ UnifyPathSeparators(file_copy);
+ if (known_paths.contains(file_copy))
+ return;
- if (auto mod = GraphicsModConfig::Create(file, source))
- m_graphics_mods.push_back(std::move(*mod));
+ if (auto mod = GraphicsModConfig::Create(file_copy, source))
+ m_graphics_mods.push_back(std::move(*mod));
+ }
+ }
};
const std::set<std::string> graphics_mod_user_directories =
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
index 4e22d5b3f8..35c8ce0a25 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
@@ -3,7 +3,11 @@
#include "VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h"
+#include "Common/JsonUtil.h"
#include "Common/Logging/Log.h"
+#include "Core/System.h"
+
+#include "VideoCommon/Resources/CustomResourceManager.h"
std::unique_ptr<CustomPipelineAction>
CustomPipelineAction::Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
@@ -15,58 +19,16 @@ std::unique_ptr<CustomPipelineAction>
CustomPipelineAction::Create(const picojson::value& json_data,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{
- std::vector<CustomPipelineAction::PipelinePassPassDescription> pipeline_passes;
-
- const auto& passes_json = json_data.get("passes");
- if (passes_json.is<picojson::array>())
- {
- for (const auto& passes_json_val : passes_json.get<picojson::array>())
- {
- CustomPipelineAction::PipelinePassPassDescription pipeline_pass;
- if (!passes_json_val.is<picojson::object>())
- {
- ERROR_LOG_FMT(VIDEO,
- "Failed to load custom pipeline action, 'passes' has an array value that "
- "is not an object!");
- return nullptr;
- }
-
- auto pass = passes_json_val.get<picojson::object>();
- if (!pass.contains("pixel_material_asset"))
- {
- ERROR_LOG_FMT(VIDEO,
- "Failed to load custom pipeline action, 'passes' value missing required "
- "field 'pixel_material_asset'");
- return nullptr;
- }
+ auto material_asset = ReadStringFromJson(json_data.get<picojson::object>(), "material_asset");
- auto pixel_material_asset_json = pass["pixel_material_asset"];
- if (!pixel_material_asset_json.is<std::string>())
- {
- ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, 'passes' field "
- "'pixel_material_asset' is not a string!");
- return nullptr;
- }
- pipeline_pass.m_pixel_material_asset = pixel_material_asset_json.to_str();
- pipeline_passes.push_back(std::move(pipeline_pass));
- }
- }
-
- if (pipeline_passes.empty())
+ if (!material_asset)
{
- ERROR_LOG_FMT(VIDEO, "Failed to load custom pipeline action, must specify at least one pass");
+ ERROR_LOG_FMT(VIDEO,
+ "Failed to load custom pipeline action, 'material_asset' does not have a value");
return nullptr;
}
- if (pipeline_passes.size() > 1)
- {
- ERROR_LOG_FMT(
- VIDEO,
- "Failed to load custom pipeline action, multiple passes are not currently supported");
- return nullptr;
- }
-
- return std::make_unique<CustomPipelineAction>(std::move(library), std::move(pipeline_passes));
+ return std::make_unique<CustomPipelineAction>(std::move(library), std::move(*material_asset));
}
CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
@@ -74,14 +36,26 @@ CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAs
{
}
-CustomPipelineAction::CustomPipelineAction(
- std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
- std::vector<PipelinePassPassDescription> pass_descriptions)
- : m_library(std::move(library)), m_passes_config(std::move(pass_descriptions))
+CustomPipelineAction::CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
+ std::string material_asset)
+ : m_library(std::move(library)), m_material_asset(std::move(material_asset))
{
- m_pipeline_passes.resize(m_passes_config.size());
}
void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted*)
{
+ // TODO
+}
+
+void CustomPipelineAction::AfterEFB(GraphicsModActionData::PostEFB* post_efb)
+{
+ if (!post_efb) [[unlikely]]
+ return;
+
+ if (m_material_asset.empty())
+ return;
+
+ auto& resource_manager = Core::System::GetInstance().GetCustomResourceManager();
+ post_efb->material =
+ resource_manager.GetPostProcessingMaterialFromAsset(m_material_asset, m_library);
}
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
index 99026b99ce..d03ccd4a6e 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
@@ -6,22 +6,15 @@
#include <memory>
#include <string>
#include <string_view>
-#include <vector>
#include <picojson.h>
#include "VideoCommon/Assets/CustomAssetLibrary.h"
-#include "VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
class CustomPipelineAction final : public GraphicsModAction
{
public:
- struct PipelinePassPassDescription
- {
- std::string m_pixel_material_asset;
- };
-
static constexpr std::string_view factory_name = "custom_pipeline";
static std::unique_ptr<CustomPipelineAction>
Create(const picojson::value& json_data,
@@ -30,11 +23,11 @@ public:
Create(std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
explicit CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
CustomPipelineAction(std::shared_ptr<VideoCommon::CustomAssetLibrary> library,
- std::vector<PipelinePassPassDescription> pass_descriptions);
+ std::string material_asset);
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
+ void AfterEFB(GraphicsModActionData::PostEFB*) override;
private:
std::shared_ptr<VideoCommon::CustomAssetLibrary> m_library;
- std::vector<PipelinePassPassDescription> m_passes_config;
- std::vector<CustomPipeline> m_pipeline_passes;
+ std::string m_material_asset;
};
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.cpp
index 1bd288512c..ca50a9a430 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.cpp
@@ -10,7 +10,7 @@ void PrintAction::OnDrawStarted(GraphicsModActionData::DrawStarted*)
INFO_LOG_FMT(VIDEO, "OnDrawStarted Called");
}
-void PrintAction::OnEFB(GraphicsModActionData::EFB* efb)
+void PrintAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{
if (!efb) [[unlikely]]
return;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h
index 82a21c152f..55bd694a6e 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/PrintAction.h
@@ -12,7 +12,7 @@ class PrintAction final : public GraphicsModAction
public:
static constexpr std::string_view factory_name = "print";
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
- void OnEFB(GraphicsModActionData::EFB*) override;
+ void BeforeEFB(GraphicsModActionData::PreEFB*) override;
void OnProjection(GraphicsModActionData::Projection*) override;
void OnProjectionAndTexture(GraphicsModActionData::Projection*) override;
void OnTextureLoad(GraphicsModActionData::TextureLoad*) override;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
index 305cd8b737..6db08b8351 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.cpp
@@ -30,7 +30,7 @@ ScaleAction::ScaleAction(Common::Vec3 scale) : m_scale(scale)
{
}
-void ScaleAction::OnEFB(GraphicsModActionData::EFB* efb)
+void ScaleAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{
if (!efb) [[unlikely]]
return;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h
index 4673ed2d18..2ed9d71393 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/ScaleAction.h
@@ -16,7 +16,7 @@ public:
static constexpr std::string_view factory_name = "scale";
static std::unique_ptr<ScaleAction> Create(const picojson::value& json_data);
explicit ScaleAction(Common::Vec3 scale);
- void OnEFB(GraphicsModActionData::EFB*) override;
+ void BeforeEFB(GraphicsModActionData::PreEFB*) override;
void OnProjection(GraphicsModActionData::Projection*) override;
void OnProjectionAndTexture(GraphicsModActionData::Projection*) override;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.cpp
index b693fef4f7..3473826fc5 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.cpp
@@ -14,7 +14,7 @@ void SkipAction::OnDrawStarted(GraphicsModActionData::DrawStarted* draw_started)
*draw_started->skip = true;
}
-void SkipAction::OnEFB(GraphicsModActionData::EFB* efb)
+void SkipAction::BeforeEFB(GraphicsModActionData::PreEFB* efb)
{
if (!efb) [[unlikely]]
return;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h
index 8cda643c5a..df397802ae 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/SkipAction.h
@@ -10,5 +10,5 @@ class SkipAction final : public GraphicsModAction
public:
static constexpr std::string_view factory_name = "skip";
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
- void OnEFB(GraphicsModActionData::EFB*) override;
+ void BeforeEFB(GraphicsModActionData::PreEFB*) override;
};
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h
index 04a371a73e..5719d550fe 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h
@@ -16,8 +16,9 @@ public:
GraphicsModAction& operator=(GraphicsModAction&&) = default;
virtual void OnDrawStarted(GraphicsModActionData::DrawStarted*) {}
- virtual void OnEFB(GraphicsModActionData::EFB*) {}
- virtual void OnXFB() {}
+ virtual void BeforeEFB(GraphicsModActionData::PreEFB*) {}
+ virtual void AfterEFB(GraphicsModActionData::PostEFB*) {}
+ virtual void BeforeXFB() {}
virtual void OnProjection(GraphicsModActionData::Projection*) {}
virtual void OnProjectionAndTexture(GraphicsModActionData::Projection*) {}
virtual void OnTextureLoad(GraphicsModActionData::TextureLoad*) {}
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h
index a4e883ffc2..e82c87e0e4 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h
@@ -12,6 +12,7 @@
#include "Common/Matrix.h"
#include "Common/SmallVector.h"
#include "VideoCommon/Assets/TextureAsset.h"
+#include "VideoCommon/Resources/MaterialResource.h"
#include "VideoCommon/ShaderGenCommon.h"
namespace GraphicsModActionData
@@ -24,7 +25,7 @@ struct DrawStarted
std::span<u8>* material_uniform_buffer;
};
-struct EFB
+struct PreEFB
{
u32 texture_width;
u32 texture_height;
@@ -33,6 +34,11 @@ struct EFB
u32* scaled_height;
};
+struct PostEFB
+{
+ VideoCommon::MaterialResource* material = nullptr;
+};
+
struct Projection
{
Common::Matrix44* matrix;
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp
index 79f19fe325..5f9cf48d52 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.cpp
@@ -7,6 +7,7 @@
#include <string_view>
#include <variant>
+#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/VariantUtil.h"
@@ -17,6 +18,7 @@
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModAsset.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
+#include "VideoCommon/GraphicsModSystem/Constants.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionFactory.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/VideoEvents.h"
@@ -36,11 +38,17 @@ public:
return;
m_action_impl->OnDrawStarted(draw_started);
}
- void OnEFB(GraphicsModActionData::EFB* efb) override
+ void BeforeEFB(GraphicsModActionData::PreEFB* efb) override
{
if (!m_mod.m_enabled)
return;
- m_action_impl->OnEFB(efb);
+ m_action_impl->BeforeEFB(efb);
+ }
+ void AfterEFB(GraphicsModActionData::PostEFB* efb) override
+ {
+ if (!m_mod.m_enabled)
+ return;
+ m_action_impl->AfterEFB(efb);
}
void OnProjection(GraphicsModActionData::Projection* projection) override
{
@@ -191,6 +199,8 @@ void GraphicsModManager::Load(const GraphicsModGroupConfig& config)
const auto& mods = config.GetMods();
auto filesystem_library = std::make_shared<VideoCommon::DirectFilesystemAssetLibrary>();
+ filesystem_library->Watch(File::GetSysDirectory() + DOLPHIN_SYSTEM_GRAPHICS_MOD_DIR);
+ filesystem_library->Watch(File::GetUserPath(D_GRAPHICSMOD_IDX));
std::map<std::string, std::vector<GraphicsTargetConfig>> group_to_targets;
for (const auto& mod : mods)
diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp
index ffb16b055a..29e215b8ea 100644
--- a/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp
+++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.cpp
@@ -43,7 +43,8 @@ void CustomResourceManager::Shutdown()
void CustomResourceManager::Reset()
{
- m_material_resources.clear();
+ m_draw_material_resources.clear();
+ m_postprocessing_material_resources.clear();
m_shader_resources.clear();
m_texture_data_resources.clear();
m_texture_sampler_resources.clear();
@@ -102,11 +103,11 @@ TextureDataResource* CustomResourceManager::GetTextureDataFromAsset(
return resource.get();
}
-MaterialResource* CustomResourceManager::GetMaterialFromAsset(
+MaterialResource* CustomResourceManager::GetDrawMaterialFromAsset(
const CustomAssetLibrary::AssetID& asset_id, const GXPipelineUid& pipeline_uid,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{
- auto& resource = m_material_resources[asset_id][PipelineToHash(pipeline_uid)];
+ auto& resource = m_draw_material_resources[asset_id][PipelineToHash(pipeline_uid)];
if (resource == nullptr)
{
resource = std::make_unique<MaterialResource>(
@@ -116,11 +117,24 @@ MaterialResource* CustomResourceManager::GetMaterialFromAsset(
return resource.get();
}
-ShaderResource*
-CustomResourceManager::GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id,
- std::size_t shader_key, const GXPipelineUid& pipeline_uid,
- const std::string& preprocessor_settings,
- std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
+MaterialResource* CustomResourceManager::GetPostProcessingMaterialFromAsset(
+ const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
+{
+ auto& resource = m_postprocessing_material_resources[asset_id];
+ if (resource == nullptr)
+ {
+ resource =
+ std::make_unique<MaterialResource>(CreateResourceContext(asset_id, std::move(library)));
+ }
+ resource->Process();
+ return resource.get();
+}
+
+ShaderResource* CustomResourceManager::GetShaderFromAsset(
+ const CustomAssetLibrary::AssetID& asset_id, std::size_t shader_key,
+ const std::optional<GXPipelineUid>& pipeline_uid, const std::string& preprocessor_settings,
+ std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
{
auto& resource = m_shader_resources[asset_id][shader_key];
if (resource == nullptr)
diff --git a/Source/Core/VideoCommon/Resources/CustomResourceManager.h b/Source/Core/VideoCommon/Resources/CustomResourceManager.h
index 04eaa46942..8bd0e75019 100644
--- a/Source/Core/VideoCommon/Resources/CustomResourceManager.h
+++ b/Source/Core/VideoCommon/Resources/CustomResourceManager.h
@@ -37,14 +37,20 @@ public:
TextureDataResource*
GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
- MaterialResource* GetMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
- const GXPipelineUid& pipeline_uid,
- std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
+ MaterialResource*
+ GetDrawMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
+ const GXPipelineUid& pipeline_uid,
+ std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
+ MaterialResource*
+ GetPostProcessingMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
+ std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
ShaderResource* GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id,
- std::size_t shader_key, const GXPipelineUid& pipeline_uid,
+ std::size_t shader_key,
+ const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_settings,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
+
TextureAndSamplerResource*
GetTextureAndSamplerFromAsset(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
@@ -59,7 +65,7 @@ private:
std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler;
using PipelineIdToMaterial = std::map<std::size_t, std::unique_ptr<MaterialResource>>;
- std::map<CustomAssetLibrary::AssetID, PipelineIdToMaterial> m_material_resources;
+ std::map<CustomAssetLibrary::AssetID, PipelineIdToMaterial> m_draw_material_resources;
using ShaderKeyToShader = std::map<std::size_t, std::unique_ptr<ShaderResource>>;
std::map<CustomAssetLibrary::AssetID, ShaderKeyToShader> m_shader_resources;
@@ -70,6 +76,9 @@ private:
std::map<CustomAssetLibrary::AssetID, std::unique_ptr<TextureAndSamplerResource>>
m_texture_sampler_resources;
+ std::map<CustomAssetLibrary::AssetID, std::unique_ptr<MaterialResource>>
+ m_postprocessing_material_resources;
+
ShaderHostConfig m_host_config;
Common::EventHook m_xfb_event;
diff --git a/Source/Core/VideoCommon/Resources/MaterialResource.cpp b/Source/Core/VideoCommon/Resources/MaterialResource.cpp
index 38486d7932..d41937d992 100644
--- a/Source/Core/VideoCommon/Resources/MaterialResource.cpp
+++ b/Source/Core/VideoCommon/Resources/MaterialResource.cpp
@@ -54,6 +54,13 @@ SamplerState CalculateSamplerAnisotropy(const SamplerState& initial_sampler)
namespace VideoCommon
{
+MaterialResource::MaterialResource(Resource::ResourceContext resource_context)
+ : Resource(std::move(resource_context))
+{
+ m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>(
+ m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
+}
+
MaterialResource::MaterialResource(Resource::ResourceContext resource_context,
const GXPipelineUid& pipeline_uid)
: Resource(std::move(resource_context)), m_uid(pipeline_uid)
@@ -61,8 +68,8 @@ MaterialResource::MaterialResource(Resource::ResourceContext resource_context,
m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>(
m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
m_uid_vertex_format_copy =
- g_gfx->CreateNativeVertexFormat(m_uid.vertex_format->GetVertexDeclaration());
- m_uid.vertex_format = m_uid_vertex_format_copy.get();
+ g_gfx->CreateNativeVertexFormat(m_uid->vertex_format->GetVertexDeclaration());
+ m_uid->vertex_format = m_uid_vertex_format_copy.get();
}
void MaterialResource::ResetData()
@@ -98,7 +105,7 @@ Resource::TaskComplete MaterialResource::CollectPrimaryData()
}
CreateTextureData(m_load_data.get());
- SetShaderKey(m_load_data.get(), &m_uid);
+ SetShaderKey(m_load_data.get(), m_uid ? &*m_uid : nullptr);
return Resource::TaskComplete::Yes;
}
@@ -140,8 +147,18 @@ Resource::TaskComplete MaterialResource::CollectDependencyData()
if (m_load_data->m_material_data->next_material_asset != "")
{
- m_load_data->m_next_material = m_resource_context.resource_manager->GetMaterialFromAsset(
- m_load_data->m_material_data->next_material_asset, m_uid, m_resource_context.asset_library);
+ if (m_uid)
+ {
+ m_load_data->m_next_material = m_resource_context.resource_manager->GetDrawMaterialFromAsset(
+ m_load_data->m_material_data->next_material_asset, *m_uid,
+ m_resource_context.asset_library);
+ }
+ else
+ {
+ m_load_data->m_next_material =
+ m_resource_context.resource_manager->GetPostProcessingMaterialFromAsset(
+ m_load_data->m_material_data->next_material_asset, m_resource_context.asset_library);
+ }
m_load_data->m_next_material->AddReference(this);
const auto data_processed = m_load_data->m_next_material->IsDataProcessed();
if (data_processed == TaskComplete::Error)
@@ -222,33 +239,66 @@ Resource::TaskComplete MaterialResource::ProcessData()
config.pixel_shader = m_shader_resource_data->GetPixelShader();
config.geometry_shader = m_shader_resource_data->GetGeometryShader();
- const auto actual_uid = ApplyDriverBugs(*m_uid);
-
- if (m_material_resource_data->m_material_data->blending_state)
- config.blending_state = *m_material_resource_data->m_material_data->blending_state;
+ if (m_uid)
+ {
+ // Draw based pipeline
+ const auto actual_uid = ApplyDriverBugs(*m_uid);
+
+ if (m_material_resource_data->m_material_data->blending_state)
+ config.blending_state = *m_material_resource_data->m_material_data->blending_state;
+ else
+ config.blending_state = actual_uid.blending_state;
+
+ if (m_material_resource_data->m_material_data->depth_state)
+ config.depth_state = *m_material_resource_data->m_material_data->depth_state;
+ else
+ config.depth_state = actual_uid.depth_state;
+
+ config.framebuffer_state = std::move(m_frame_buffer_state);
+ config.framebuffer_state.additional_color_attachment_count = 0;
+
+ config.rasterization_state = actual_uid.rasterization_state;
+ if (m_material_resource_data->m_material_data->cull_mode)
+ {
+ config.rasterization_state.cull_mode =
+ *m_material_resource_data->m_material_data->cull_mode;
+ }
+
+ config.vertex_format = actual_uid.vertex_format;
+ config.usage = AbstractPipelineUsage::GX;
+ }
else
- config.blending_state = actual_uid.blending_state;
+ {
+ // Post processing based pipeline
- if (m_material_resource_data->m_material_data->depth_state)
- config.depth_state = *m_material_resource_data->m_material_data->depth_state;
- else
- config.depth_state = actual_uid.depth_state;
+ // Many of these properties don't make sense to replace but we expose them for draw
+ // based materials, might as well allow them to be used if the user wants
- config.framebuffer_state = std::move(m_frame_buffer_state);
- config.framebuffer_state.additional_color_attachment_count = 0;
+ if (m_material_resource_data->m_material_data->blending_state)
+ config.blending_state = *m_material_resource_data->m_material_data->blending_state;
+ else
+ config.blending_state = RenderState::GetNoBlendingBlendState();
- config.rasterization_state = actual_uid.rasterization_state;
- if (m_material_resource_data->m_material_data->cull_mode)
- {
- config.rasterization_state.cull_mode =
- *m_material_resource_data->m_material_data->cull_mode;
- }
+ if (m_material_resource_data->m_material_data->depth_state)
+ config.depth_state = *m_material_resource_data->m_material_data->depth_state;
+ else
+ config.depth_state = RenderState::GetNoDepthTestingDepthState();
- config.vertex_format = actual_uid.vertex_format;
- config.usage = AbstractPipelineUsage::GX;
+ config.framebuffer_state = RenderState::GetRGBA8FramebufferState();
- m_material_resource_data->m_pipeline = g_gfx->CreatePipeline(config);
+ config.rasterization_state =
+ RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles);
+ if (m_material_resource_data->m_material_data->cull_mode)
+ {
+ config.rasterization_state.cull_mode =
+ *m_material_resource_data->m_material_data->cull_mode;
+ }
+ config.vertex_format = nullptr;
+ config.usage = AbstractPipelineUsage::Utility;
+ }
+
+ m_material_resource_data->m_pipeline = g_gfx->CreatePipeline(config);
if (m_material_resource_data->m_pipeline)
{
WriteUniforms(m_material_resource_data.get());
@@ -268,7 +318,7 @@ Resource::TaskComplete MaterialResource::ProcessData()
if (!m_processing_load_data)
{
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
- m_load_data, std::move(shader_data), &m_uid,
+ m_load_data, std::move(shader_data), m_uid ? &*m_uid : nullptr,
g_framebuffer_manager->GetEFBFramebufferState());
// We don't need priority, that is already handled by the resource system
@@ -360,7 +410,8 @@ void MaterialResource::SetShaderKey(Data* data, GXPipelineUid* uid)
XXH3_INITSTATE(&shader_key_hash);
XXH3_64bits_reset_withSeed(&shader_key_hash, static_cast<XXH64_hash_t>(1));
- UpdateHashWithPipeline(*uid, &shader_key_hash);
+ if (uid)
+ UpdateHashWithPipeline(*uid, &shader_key_hash);
XXH3_64bits_update(&shader_key_hash, data->m_preprocessor_settings.c_str(),
data->m_preprocessor_settings.size());
diff --git a/Source/Core/VideoCommon/Resources/MaterialResource.h b/Source/Core/VideoCommon/Resources/MaterialResource.h
index 4842f000ea..6f4835927f 100644
--- a/Source/Core/VideoCommon/Resources/MaterialResource.h
+++ b/Source/Core/VideoCommon/Resources/MaterialResource.h
@@ -5,6 +5,7 @@
#include <atomic>
#include <memory>
+#include <optional>
#include <string_view>
#include <variant>
#include <vector>
@@ -27,6 +28,7 @@ namespace VideoCommon
class MaterialResource final : public Resource
{
public:
+ explicit MaterialResource(Resource::ResourceContext resource_context);
MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid);
struct TextureLikeReference
@@ -93,7 +95,9 @@ private:
// Note: asset cache owns the asset, we access as a reference
MaterialAsset* m_material_asset = nullptr;
- GXPipelineUid m_uid;
+ // If provided, denotes this material will be used as a custom draw material.
+ // If not provided, denotes this will be used as an efb post processing material.
+ std::optional<GXPipelineUid> m_uid;
std::unique_ptr<NativeVertexFormat> m_uid_vertex_format_copy;
};
} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/Resources/ShaderResource.cpp b/Source/Core/VideoCommon/Resources/ShaderResource.cpp
index 47600f2c6a..08aa994055 100644
--- a/Source/Core/VideoCommon/Resources/ShaderResource.cpp
+++ b/Source/Core/VideoCommon/Resources/ShaderResource.cpp
@@ -10,6 +10,7 @@
#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/Assets/CustomAssetCache.h"
#include "VideoCommon/AsyncShaderCompiler.h"
+#include "VideoCommon/FramebufferShaderGen.h"
#include "VideoCommon/GeometryShaderGen.h"
#include "VideoCommon/PipelineUtils.h"
#include "VideoCommon/PixelShaderGen.h"
@@ -20,20 +21,223 @@ namespace VideoCommon
{
namespace
{
-std::unique_ptr<AbstractShader>
-CompileGeometryShader(const GeometryShaderUid& uid, APIType api_type, ShaderHostConfig host_config)
+// TODO: the uniform buffer is combined due to the utility path only having a single
+// set of constants, this isn't ideal (both for the end user, where it's more readable
+// to see 'custom_uniforms' before variables and from Dolphin because the
+// standard uniforms have to be packed with the custom ones
+void GeneratePostProcessUniformOutput(ShaderCode& shader_source, std::string_view block_name,
+ std::string_view uniforms)
+{
+ shader_source.Write("UBO_BINDING(std140, 1) uniform {} {{\n", block_name);
+ shader_source.Write("\tvec4 source_resolution;\n");
+ shader_source.Write("\tvec4 target_resolution;\n");
+ shader_source.Write("\tvec4 window_resolution;\n");
+ shader_source.Write("\tvec4 source_region;\n");
+ shader_source.Write("\tint source_layer;\n");
+ shader_source.Write("\tint source_layer_pad1;\n");
+ shader_source.Write("\tint source_layer_pad2;\n");
+ shader_source.Write("\tint source_layer_pad3;\n");
+ shader_source.Write("\tuint time;\n");
+ shader_source.Write("\tuint time_pad1;\n");
+ shader_source.Write("\tuint time_pad2;\n");
+ shader_source.Write("\tuint time_pad3;\n");
+ shader_source.Write("\tint graphics_api;\n");
+ shader_source.Write("\tint graphics_api_pad1;\n");
+ shader_source.Write("\tint graphics_api_pad2;\n");
+ shader_source.Write("\tint graphics_api_pad3;\n");
+ shader_source.Write("\tuint efb_scale;\n");
+ shader_source.Write("\tuint efb_scale_pad1;\n");
+ shader_source.Write("\tuint efb_scale_pad2;\n");
+ shader_source.Write("\tuint efb_scale_pad3;\n");
+ if (!uniforms.empty())
+ {
+ shader_source.Write("{}", uniforms);
+ }
+ shader_source.Write("}};\n");
+}
+
+// TODO: move this to a more standard post processing file
+// once post processing has been cleaned up
+void GeneratePostProcessingVertexShader(ShaderCode& shader_source,
+ const CustomVertexContents& custom_contents)
+{
+ // Note: if blocks are the same, they need to match for the OpenGL backend
+ GeneratePostProcessUniformOutput(shader_source, "PSBlock", custom_contents.uniforms);
+
+ // Define some defines that are shared with custom draw shaders,
+ // so that a common shader code could possibly be used in both
+ shader_source.Write("#define HAS_COLOR_0 0\n");
+ shader_source.Write("#define HAS_COLOR_1 0\n");
+ shader_source.Write("#define HAS_NORMAL 0\n");
+ shader_source.Write("#define HAS_BINORMAL 0\n");
+ shader_source.Write("#define HAS_TANGENT 0\n");
+ shader_source.Write("#define HAS_TEXTURE_COORD_0 1\n");
+ for (u32 i = 1; i < 8; i++)
+ {
+ shader_source.Write("#define HAS_TEXTURE_COORD_{} 0\n", i);
+ }
+
+ // Write the common structs, might want to consider
+ // moving these to another location?
+ shader_source.Write("struct DolphinVertexInput\n");
+ shader_source.Write("{{\n");
+ shader_source.Write("\tvec4 position;\n");
+ shader_source.Write("\tvec3 texture_coord_0;\n");
+ shader_source.Write("}};\n\n");
+
+ shader_source.Write("struct DolphinVertexOutput\n");
+ shader_source.Write("{{\n");
+ shader_source.Write("\tvec4 position;\n");
+ shader_source.Write("\tvec3 texture_coord_0;\n");
+ shader_source.Write("}};\n\n");
+
+ constexpr std::string_view emulated_vertex_definition =
+ "void dolphin_process_emulated_vertex(in DolphinVertexInput vertex_input, out "
+ "DolphinVertexOutput vertex_output)";
+ shader_source.Write("{}\n", emulated_vertex_definition);
+ shader_source.Write("{{\n");
+ shader_source.Write("\tvertex_output.position = vertex_input.position;\n");
+ shader_source.Write("\tvertex_output.texture_coord_0 = vertex_input.texture_coord_0;\n");
+ shader_source.Write("}}\n");
+
+ if (custom_contents.shader.empty())
+ {
+ shader_source.Write(
+ "void process_vertex(in DolphinVertexInput vertex_input, out DolphinVertexOutput "
+ "vertex_output)\n");
+ shader_source.Write("{{\n");
+
+ shader_source.Write("\tdolphin_process_emulated_vertex(vertex_input, vertex_output);\n");
+
+ shader_source.Write("}}\n");
+ }
+ else
+ {
+ shader_source.Write("{}\n", custom_contents.shader);
+ }
+
+ if (g_backend_info.bSupportsGeometryShaders)
+ {
+ shader_source.Write("VARYING_LOCATION(0) out VertexData {{\n");
+ shader_source.Write("\tvec3 v_tex0;\n");
+ shader_source.Write("}};\n");
+ }
+ else
+ {
+ shader_source.Write("VARYING_LOCATION(0) out vec3 v_tex0;\n");
+ }
+
+ shader_source.Write("void main()\n");
+ shader_source.Write("{{\n");
+ shader_source.Write("\tDolphinVertexInput vertex_input;\n");
+ shader_source.Write("\tvec3 vert = vec3(float((gl_VertexID << 1) & 2), "
+ "float(gl_VertexID & 2), 0.0f);\n");
+ shader_source.Write("\tvertex_input.position = vec4(vert.xy * "
+ "float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n");
+ shader_source.Write("\tvertex_input.texture_coord_0 = vec3(source_region.xy + "
+ "(source_region.zw * vert.xy), 0.0f);\n");
+ shader_source.Write("\tDolphinVertexOutput vertex_output;\n");
+ shader_source.Write("\tprocess_vertex(vertex_input, vertex_output);\n");
+ shader_source.Write("\tgl_Position = vertex_output.position;\n");
+ shader_source.Write("\tv_tex0 = vertex_output.texture_coord_0;\n");
+
+ // NDC space is flipped in Vulkan
+ if (g_backend_info.api_type == APIType::Vulkan)
+ {
+ shader_source.Write("\tgl_Position.y = -gl_Position.y;\n");
+ }
+
+ shader_source.Write("}}\n");
+}
+
+void GeneratePostProcessingPixelShader(ShaderCode& shader_source,
+ const CustomPixelContents& custom_contents)
+{
+ GeneratePostProcessUniformOutput(shader_source, "PSBlock", custom_contents.uniforms);
+
+ shader_source.Write("SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n");
+
+ if (g_backend_info.bSupportsGeometryShaders)
+ {
+ shader_source.Write("VARYING_LOCATION(0) in VertexData {{\n");
+ shader_source.Write("\tvec3 v_tex0;\n");
+ shader_source.Write("}};\n");
+ }
+ else
+ {
+ shader_source.Write("VARYING_LOCATION(0) in float3 v_tex0;\n");
+ }
+
+ shader_source.Write("FRAGMENT_OUTPUT_LOCATION(0) out vec4 ocol0;\n");
+
+ shader_source.Write("struct DolphinFragmentInput\n");
+ shader_source.Write("{{\n");
+ for (u32 i = 0; i < 1; i++)
+ {
+ shader_source.Write("\tvec3 tex{};\n", i);
+ }
+ shader_source.Write("\n");
+
+ shader_source.Write("}};\n\n");
+
+ shader_source.Write("struct DolphinFragmentOutput\n");
+ shader_source.Write("{{\n");
+ shader_source.Write("\tvec4 main;\n");
+ shader_source.Write("}};\n\n");
+
+ constexpr std::string_view emulated_fragment_definition =
+ "void dolphin_process_emulated_fragment(in DolphinFragmentInput frag_input, out "
+ "DolphinFragmentOutput frag_output)";
+ shader_source.Write("{}\n", emulated_fragment_definition);
+ shader_source.Write("{{\n");
+ shader_source.Write("\tfrag_output.main = texture(samp0, frag_input.tex0);\n");
+ shader_source.Write("}}\n");
+
+ if (custom_contents.shader.empty())
+ {
+ shader_source.Write(
+ "void process_fragment(in DolphinFragmentInput frag_input, out DolphinFragmentOutput "
+ "frag_output)\n");
+ shader_source.Write("{{\n");
+
+ shader_source.Write("\tdolphin_process_emulated_fragment(frag_input, frag_output);\n");
+
+ shader_source.Write("}}\n");
+ }
+ else
+ {
+ shader_source.Write("{}\n", custom_contents.shader);
+ }
+
+ shader_source.Write("void main()\n");
+ shader_source.Write("{{\n");
+ shader_source.Write("\tDolphinFragmentInput frag_input;\n");
+ shader_source.Write("\tfrag_input.tex0 = v_tex0;\n");
+ shader_source.Write("\tDolphinFragmentOutput frag_output;\n");
+ shader_source.Write("\tprocess_fragment(frag_input, frag_output);\n");
+ shader_source.Write("\tocol0 = frag_output.main;\n");
+ shader_source.Write("}}\n");
+}
+
+std::unique_ptr<AbstractShader> CompileGeometryShader(GeometryShaderUid* uid, APIType api_type,
+ ShaderHostConfig host_config)
{
+ if (!uid)
+ {
+ return g_gfx->CreateShaderFromSource(
+ ShaderStage::Geometry, FramebufferShaderGen::GeneratePassthroughGeometryShader(1, 0),
+ nullptr, "Custom Post Processing Geometry Shader");
+ }
+
const ShaderCode source_code =
- GenerateGeometryShaderCode(api_type, host_config, uid.GetUidData());
+ GenerateGeometryShaderCode(api_type, host_config, uid->GetUidData());
return g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr,
- fmt::format("Geometry shader: {}", *uid.GetUidData()));
+ fmt::format("Geometry shader: {}", *uid->GetUidData()));
}
-std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
- std::string_view preprocessor_settings,
- APIType api_type,
- const ShaderHostConfig& host_config,
- RasterSurfaceShaderData* shader_data)
+std::unique_ptr<AbstractShader>
+CompilePixelShader(PixelShaderUid* uid, std::string_view preprocessor_settings, APIType api_type,
+ const ShaderHostConfig& host_config, RasterSurfaceShaderData* shader_data)
{
ShaderCode shader_code;
@@ -89,19 +293,25 @@ std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
// Compile the shader
CustomPixelContents contents{.shader = shader_code.GetBuffer(),
.uniforms = uniform_code.GetBuffer()};
- const ShaderCode source_code =
- GeneratePixelShaderCode(api_type, host_config, uid.GetUidData(), contents);
+
+ ShaderCode source_code;
+ if (uid)
+ {
+ source_code = GeneratePixelShaderCode(api_type, host_config, uid->GetUidData(), contents);
+ }
+ else
+ {
+ GeneratePostProcessingPixelShader(source_code, contents);
+ }
ShaderIncluder* shader_includer =
shader_data->shader_includer ? &*shader_data->shader_includer : nullptr;
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), shader_includer,
"Custom Pixel Shader");
}
-std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
- std::string_view preprocessor_settings,
- APIType api_type,
- const ShaderHostConfig& host_config,
- const RasterSurfaceShaderData& shader_data)
+std::unique_ptr<AbstractShader>
+CompileVertexShader(VertexShaderUid* uid, std::string_view preprocessor_settings, APIType api_type,
+ const ShaderHostConfig& host_config, const RasterSurfaceShaderData& shader_data)
{
ShaderCode shader_code;
@@ -157,14 +367,22 @@ std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
// Compile the shader
CustomVertexContents contents{.shader = shader_code.GetBuffer(),
.uniforms = uniform_code.GetBuffer()};
- const ShaderCode source_code =
- GenerateVertexShaderCode(api_type, host_config, uid.GetUidData(), contents);
+
+ ShaderCode source_code;
+ if (uid)
+ {
+ source_code = GenerateVertexShaderCode(api_type, host_config, uid->GetUidData(), contents);
+ }
+ else
+ {
+ GeneratePostProcessingVertexShader(source_code, contents);
+ }
return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), nullptr,
"Custom Vertex Shader");
}
} // namespace
ShaderResource::ShaderResource(Resource::ResourceContext resource_context,
- const GXPipelineUid& pipeline_uid,
+ const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_setting,
const ShaderHostConfig& shader_host_config)
: Resource(std::move(resource_context)), m_shader_host_config{.bits = shader_host_config.bits},
@@ -257,24 +475,46 @@ Resource::TaskComplete ShaderResource::ProcessData()
bool Compile() override
{
const ShaderHostConfig shader_host_config{.bits = m_shader_bits};
- auto actual_uid = ApplyDriverBugs(*m_uid);
-
- ClearUnusedPixelShaderUidBits(g_backend_info.api_type, shader_host_config,
- &actual_uid.ps_uid);
- m_resource_data->m_needs_geometry_shader = shader_host_config.backend_geometry_shaders &&
- !actual_uid.gs_uid.GetUidData()->IsPassthrough();
-
- if (m_resource_data->m_needs_geometry_shader)
+ if (m_uid)
+ {
+ // Draw based shader
+ auto actual_uid = ApplyDriverBugs(*m_uid);
+
+ ClearUnusedPixelShaderUidBits(g_backend_info.api_type, shader_host_config,
+ &actual_uid.ps_uid);
+ m_resource_data->m_needs_geometry_shader = shader_host_config.backend_geometry_shaders &&
+ !actual_uid.gs_uid.GetUidData()->IsPassthrough();
+
+ if (m_resource_data->m_needs_geometry_shader)
+ {
+ m_resource_data->m_geometry_shader = CompileGeometryShader(
+ &actual_uid.gs_uid, g_backend_info.api_type, shader_host_config);
+ }
+ m_resource_data->m_pixel_shader =
+ CompilePixelShader(&actual_uid.ps_uid, m_preprocessor_settings, g_backend_info.api_type,
+ shader_host_config, m_resource_data->m_shader_data.get());
+ m_resource_data->m_vertex_shader = CompileVertexShader(
+ &actual_uid.vs_uid, m_preprocessor_settings, g_backend_info.api_type,
+ shader_host_config, *m_resource_data->m_shader_data);
+ }
+ else
{
- m_resource_data->m_geometry_shader =
- CompileGeometryShader(actual_uid.gs_uid, g_backend_info.api_type, shader_host_config);
+ // Post processing based shader
+
+ m_resource_data->m_needs_geometry_shader =
+ shader_host_config.backend_geometry_shaders && shader_host_config.stereo;
+ if (m_resource_data->m_needs_geometry_shader)
+ {
+ m_resource_data->m_geometry_shader =
+ CompileGeometryShader(nullptr, g_backend_info.api_type, shader_host_config);
+ }
+ m_resource_data->m_pixel_shader =
+ CompilePixelShader(nullptr, m_preprocessor_settings, g_backend_info.api_type,
+ shader_host_config, m_resource_data->m_shader_data.get());
+ m_resource_data->m_vertex_shader =
+ CompileVertexShader(nullptr, m_preprocessor_settings, g_backend_info.api_type,
+ shader_host_config, *m_resource_data->m_shader_data);
}
- m_resource_data->m_pixel_shader =
- CompilePixelShader(actual_uid.ps_uid, m_preprocessor_settings, g_backend_info.api_type,
- shader_host_config, m_resource_data->m_shader_data.get());
- m_resource_data->m_vertex_shader =
- CompileVertexShader(actual_uid.vs_uid, m_preprocessor_settings, g_backend_info.api_type,
- shader_host_config, *m_resource_data->m_shader_data);
m_resource_data->m_processing_finished = true;
return true;
}
@@ -291,7 +531,7 @@ Resource::TaskComplete ShaderResource::ProcessData()
{
std::string_view preprocessor_settings = m_preprocessor_settings;
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
- m_load_data, &m_uid, m_shader_host_config.bits, preprocessor_settings);
+ m_load_data, m_uid ? &*m_uid : nullptr, m_shader_host_config.bits, preprocessor_settings);
// We don't need priority, that is already handled by the resource system
m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0);
diff --git a/Source/Core/VideoCommon/Resources/ShaderResource.h b/Source/Core/VideoCommon/Resources/ShaderResource.h
index 0f91cd3714..2f35087aab 100644
--- a/Source/Core/VideoCommon/Resources/ShaderResource.h
+++ b/Source/Core/VideoCommon/Resources/ShaderResource.h
@@ -4,6 +4,7 @@
#pragma once
#include <atomic>
+#include <optional>
#include "VideoCommon/Resources/Resource.h"
@@ -16,7 +17,8 @@ namespace VideoCommon
class ShaderResource final : public Resource
{
public:
- ShaderResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid,
+ ShaderResource(Resource::ResourceContext resource_context,
+ const std::optional<GXPipelineUid>& pipeline_uid,
const std::string& preprocessor_settings,
const ShaderHostConfig& shader_host_config);
@@ -61,7 +63,10 @@ private:
bool m_processing_load_data = false;
ShaderHostConfig m_shader_host_config;
- GXPipelineUid m_uid;
+
+ // If provided, denotes this shader will be used as a custom draw shader.
+ // If not provided, denotes this will be used as an efb post processing shader.
+ std::optional<GXPipelineUid> m_uid;
std::string m_preprocessor_settings;
};
} // namespace VideoCommon
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 4d1d795938..6fc13c5fba 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -2258,16 +2258,16 @@ void TextureCacheBase::CopyRenderTargetToTexture(
{
for (const auto& action : g_graphics_mod_manager->GetXFBActions(info))
{
- action->OnXFB();
+ action->BeforeXFB();
}
}
else
{
bool skip = false;
- GraphicsModActionData::EFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h};
+ GraphicsModActionData::PreEFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h};
for (const auto& action : g_graphics_mod_manager->GetEFBActions(info))
{
- action->OnEFB(&efb);
+ action->BeforeEFB(&efb);
}
if (skip == true)
{
@@ -2329,6 +2329,26 @@ void TextureCacheBase::CopyRenderTargetToTexture(
isIntensity, gamma, clamp_top, clamp_bottom,
GetVRAMCopyFilterCoefficients(filter_coefficients));
+ if (g_ActiveConfig.bGraphicMods)
+ {
+ FBInfo info;
+ info.m_width = tex_w;
+ info.m_height = tex_h;
+ info.m_texture_format = baseFormat;
+ if (!is_xfb_copy)
+ {
+ GraphicsModActionData::PostEFB efb;
+ for (const auto& action : g_graphics_mod_manager->GetEFBActions(info))
+ {
+ action->AfterEFB(&efb);
+ if (efb.material)
+ {
+ ApplyMaterialToCacheEntry(*efb.material, entry.get());
+ }
+ }
+ }
+ }
+
if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods))
{
const std::string id = fmt::format("{}x{}", tex_w, tex_h);
@@ -3035,6 +3055,110 @@ bool TextureCacheBase::DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, c
return true;
}
+void TextureCacheBase::ApplyMaterialToCacheEntry(const VideoCommon::MaterialResource& material,
+ TCacheEntry* entry)
+{
+ const auto material_data = material.GetData();
+ if (!material_data) [[unlikely]]
+ return;
+
+ // Make a copy, we can't write to our texture and use its framebuffer
+ // at the same time
+ auto new_entry = AllocateCacheEntry(entry->texture->GetConfig());
+ new_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format,
+ entry->should_force_safe_hashing);
+ new_entry->SetDimensions(entry->native_width, entry->native_height, 1);
+ new_entry->SetEfbCopy(entry->memory_stride);
+ new_entry->may_have_overlapping_textures = false;
+ new_entry->frameCount = FRAMECOUNT_INVALID;
+
+ g_gfx->BeginUtilityDrawing();
+ entry->texture->FinishedRendering();
+
+ const auto custom_uniforms = material_data->GetUniforms();
+
+ // Set up uniforms.
+ // TODO: this struct should be shared with post processing
+ struct Uniforms
+ {
+ std::array<float, 4> source_resolution;
+ std::array<float, 4> target_resolution;
+ std::array<float, 4> window_resolution;
+ std::array<float, 4> source_rectangle;
+ s32 source_layer;
+ s32 source_layer_pad[3];
+ u32 time;
+ u32 time_pad[3];
+ s32 graphics_api;
+ s32 graphics_api_pad[3];
+ u32 efb_scale;
+ u32 efb_scale_pad[3];
+ } uniforms;
+
+ const float rcp_src_width = 1.0f / entry->texture->GetWidth();
+ const float rcp_src_height = 1.0f / entry->texture->GetHeight();
+
+ uniforms.source_resolution = {static_cast<float>(entry->texture->GetWidth()),
+ static_cast<float>(entry->texture->GetHeight()), rcp_src_width,
+ rcp_src_height};
+
+ // The target resolution is the same here, since we're
+ // injecting into the texture
+ uniforms.target_resolution = uniforms.source_resolution;
+
+ const auto present_rect = g_presenter->GetTargetRectangle();
+ uniforms.window_resolution = {static_cast<float>(present_rect.GetWidth()),
+ static_cast<float>(present_rect.GetHeight()),
+ 1.0f / static_cast<float>(present_rect.GetWidth()),
+ 1.0f / static_cast<float>(present_rect.GetHeight())};
+
+ uniforms.source_rectangle = {0, 0, 1, 1};
+ uniforms.source_layer = 0;
+ uniforms.time = 0;
+ uniforms.graphics_api = static_cast<s32>(g_backend_info.api_type);
+ uniforms.efb_scale = g_framebuffer_manager->GetEFBScale();
+
+ Common::UniqueBuffer<u8> uniform_buffer(custom_uniforms.size() + sizeof(uniforms));
+ std::memcpy(uniform_buffer.data(), &uniforms, sizeof(uniforms));
+ std::memcpy(uniform_buffer.data() + sizeof(uniforms), custom_uniforms.data(),
+ custom_uniforms.size());
+ g_vertex_manager->UploadUtilityUniforms(uniform_buffer.data(),
+ static_cast<u32>(uniform_buffer.size()));
+
+ // Set framebuffer and viewport based on new entry
+ g_gfx->SetAndDiscardFramebuffer(new_entry->framebuffer.get());
+ g_gfx->SetViewportAndScissor(new_entry->framebuffer->GetRect());
+ g_gfx->SetPipeline(material_data->GetPipeline());
+
+ g_gfx->SetTexture(0, entry->texture.get());
+ g_gfx->SetSamplerState(0, RenderState::GetPointSamplerState());
+
+ for (const auto texture : material_data->GetTextures())
+ {
+ g_gfx->SetTexture(texture.sampler_index, texture.texture);
+ g_gfx->SetSamplerState(texture.sampler_index, texture.sampler);
+ }
+
+ g_gfx->Draw(0, 3);
+ g_gfx->EndUtilityDrawing();
+
+ // Finish rendering new entry
+ new_entry->texture->FinishedRendering();
+
+ // Swap new entry and existing entry
+ std::swap(entry->texture, new_entry->texture);
+ std::swap(entry->framebuffer, new_entry->framebuffer);
+
+ // Return old entry to pool for use in another pass
+ // or future functionality
+ ReleaseToPool(new_entry.get());
+
+ if (auto* const next_material = material_data->GetNextMaterial(); next_material)
+ {
+ ApplyMaterialToCacheEntry(*next_material, entry);
+ }
+}
+
u32 TCacheEntry::BytesPerRow() const
{
// RGBA takes two cache lines per block; all others take one
diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h
index 1aa67dede5..d596f84e5e 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.h
+++ b/Source/Core/VideoCommon/TextureCacheBase.h
@@ -41,6 +41,7 @@ namespace VideoCommon
{
class CustomTextureData;
class GameTextureAsset;
+class MaterialResource;
} // namespace VideoCommon
constexpr std::string_view EFB_DUMP_PREFIX = "efb1";
@@ -407,6 +408,8 @@ private:
void DoSaveState(PointerWrap& p);
void DoLoadState(PointerWrap& p);
+ void ApplyMaterialToCacheEntry(const VideoCommon::MaterialResource& material, TCacheEntry* entry);
+
// m_textures_by_address is the authoritive version of what's actually "in" the texture cache
// but it's possible for invalidated TCache entries to live on elsewhere
TexAddrCache m_textures_by_address;