summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authoriwubcode <iwubcode@users.noreply.github.com>2025-11-17 18:40:21 -0600
committeriwubcode <iwubcode@users.noreply.github.com>2026-03-26 20:13:16 -0500
commit1f72403ec7ac026ffee239a85c5b98a54742ab15 (patch)
treed73201576506f46e1e7965c5eff2005495a6d715 /Source/Core/VideoCommon
parent7bfd43eb1aed359222a2ba0dde8c191dc8bce245 (diff)
VideoCommon: optionally apply materials to EFBs
After an EFB operation is turned into a texture, if there is a graphics mod action that provides a custom material, update the EFB texture with the material
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.cpp124
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.h3
2 files changed, 127 insertions, 0 deletions
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 6144307e6e..6fc13c5fba 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -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;