diff options
| author | iwubcode <iwubcode@users.noreply.github.com> | 2025-11-17 18:40:21 -0600 |
|---|---|---|
| committer | iwubcode <iwubcode@users.noreply.github.com> | 2026-03-26 20:13:16 -0500 |
| commit | 1f72403ec7ac026ffee239a85c5b98a54742ab15 (patch) | |
| tree | d73201576506f46e1e7965c5eff2005495a6d715 /Source/Core/VideoCommon | |
| parent | 7bfd43eb1aed359222a2ba0dde8c191dc8bce245 (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.cpp | 124 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureCacheBase.h | 3 |
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; |
