diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 3c0b372fed..41e5b163fc 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -4,6 +4,7 @@ #include "VideoCommon/TextureCacheBase.h" #include +#include #include #include #include @@ -39,8 +40,7 @@ #include "VideoCommon/Assets/CustomTextureData.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/FramebufferManager.h" -#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" +#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModBackend.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/HiresTextures.h" #include "VideoCommon/OpcodeDecoding.h" @@ -135,6 +135,31 @@ void TextureCacheBase::Invalidate() FlushEFBCopies(); TMEM::InvalidateAll(); + if (g_ActiveConfig.bGraphicMods) + { + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + for (auto& tex : m_textures_by_address) + { + const auto& entry = tex.second; + if (entry->is_efb_copy) + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::EFB, + entry->texture_info_name); + } + else if (entry->is_xfb_copy) + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::XFB, + entry->texture_info_name); + } + else + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::Normal, + entry->texture_info_name); + } + } + } + for (auto& bind : m_bound_textures) bind.reset(); m_textures_by_hash.clear(); @@ -270,15 +295,6 @@ bool TextureCacheBase::DidLinkedAssetsChange(const TCacheEntry& entry) } } - for (const auto& cached_asset : entry.linked_asset_dependencies) - { - if (cached_asset.m_asset) - { - if (cached_asset.m_asset->GetLastLoadedTime() > cached_asset.m_cached_write_time) - return true; - } - } - return false; } @@ -1292,16 +1308,21 @@ TCacheEntry* TextureCacheBase::LoadImpl(const TextureInfo& texture_info, bool fo return nullptr; entry->frameCount = FRAMECOUNT_INVALID; - if (entry->texture_info_name.empty() && g_ActiveConfig.bGraphicMods) + if (g_ActiveConfig.bGraphicMods) { - entry->texture_info_name = texture_info.CalculateTextureName().GetFullName(); - - GraphicsModActionData::TextureLoad texture_load{entry->texture_info_name}; - for (const auto& action : - g_graphics_mod_manager->GetTextureLoadActions(entry->texture_info_name)) + if (entry->texture_info_name.empty()) { - action->OnTextureLoad(&texture_load); + entry->texture_info_name = texture_info.CalculateTextureName().GetFullName(); } + + GraphicsModSystem::TextureView texture; + texture.hash_name = entry->texture_info_name; + texture.texture_data = entry->texture.get(); + texture.texture_type = GraphicsModSystem::TextureType::Normal; + texture.unit = texture_info.GetStage(); + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().OnTextureLoad(texture); } m_bound_textures[texture_info.GetStage()] = entry; @@ -1587,42 +1608,6 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp } } - std::vector> additional_dependencies; - - std::string texture_name = ""; - - if (g_ActiveConfig.bGraphicMods) - { - u32 height = texture_info.GetRawHeight(); - u32 width = texture_info.GetRawWidth(); - if (hires_texture) - { - auto asset = hires_texture->GetAsset(); - if (asset) - { - auto data = asset->GetData(); - if (data) - { - if (!data->m_texture.m_slices.empty()) - { - if (!data->m_texture.m_slices[0].m_levels.empty()) - { - height = data->m_texture.m_slices[0].m_levels[0].height; - width = data->m_texture.m_slices[0].m_levels[0].width; - } - } - } - } - } - texture_name = texture_info.CalculateTextureName().GetFullName(); - GraphicsModActionData::TextureCreate texture_create{ - texture_name, width, height, &cached_game_assets, &additional_dependencies}; - for (const auto& action : g_graphics_mod_manager->GetTextureCreateActions(texture_name)) - { - action->OnTextureCreate(&texture_create); - } - } - data_for_assets.reserve(cached_game_assets.size()); for (auto& cached_asset : cached_game_assets) { @@ -1644,8 +1629,20 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp texture_info, textureCacheSafetyColorSampleSize, std::move(data_for_assets), has_arbitrary_mipmaps, skip_texture_dump); entry->linked_game_texture_assets = std::move(cached_game_assets); - entry->linked_asset_dependencies = std::move(additional_dependencies); - entry->texture_info_name = std::move(texture_name); + + if (g_ActiveConfig.bGraphicMods) + { + entry->texture_info_name = texture_info.CalculateTextureName().GetFullName(); + + GraphicsModSystem::TextureView texture; + texture.hash_name = entry->texture_info_name; + texture.texture_data = entry->texture.get(); + texture.texture_type = GraphicsModSystem::TextureType::Normal; + texture.unit = texture_info.GetStage(); + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().OnTextureCreate(texture); + } return entry; } @@ -2299,36 +2296,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( return; } - 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) - { - for (const auto& action : g_graphics_mod_manager->GetXFBActions(info)) - { - action->OnXFB(); - } - } - else - { - bool skip = false; - GraphicsModActionData::EFB 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); - } - if (skip == true) - { - if (copy_to_ram) - UninitializeEFBMemory(dst, dstStride, bytes_per_row, num_blocks_y); - return; - } - } - } - if (dstStride < bytes_per_row) { // This kind of efb copy results in a scrambled image. @@ -2382,7 +2349,7 @@ void TextureCacheBase::CopyRenderTargetToTexture( if (is_xfb_copy && (g_ActiveConfig.bDumpXFBTarget || g_ActiveConfig.bGraphicMods)) { - const std::string id = fmt::format("{}x{}", tex_w, tex_h); + const std::string id = fmt::format("n{:06}_{}x{}", xfb_count++, tex_w, tex_h); if (g_ActiveConfig.bGraphicMods) { entry->texture_info_name = fmt::format("{}_{}", XFB_DUMP_PREFIX, id); @@ -2390,9 +2357,8 @@ void TextureCacheBase::CopyRenderTargetToTexture( if (g_ActiveConfig.bDumpXFBTarget) { - entry->texture->Save(fmt::format("{}{}_n{:06}_{}.png", - File::GetUserPath(D_DUMPTEXTURES_IDX), XFB_DUMP_PREFIX, - xfb_count++, id), + entry->texture->Save(fmt::format("{}{}_{}.png", File::GetUserPath(D_DUMPTEXTURES_IDX), + XFB_DUMP_PREFIX, id), 0); } } @@ -2413,6 +2379,23 @@ void TextureCacheBase::CopyRenderTargetToTexture( 0); } } + + if (g_ActiveConfig.bGraphicMods) + { + GraphicsModSystem::TextureView texture; + texture.hash_name = entry->texture_info_name; + texture.texture_data = entry->texture.get(); + if (is_xfb_copy) + { + texture.texture_type = GraphicsModSystem::TextureType::XFB; + } + else + { + texture.texture_type = GraphicsModSystem::TextureType::EFB; + } + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().OnTextureCreate(texture); + } } } @@ -2459,6 +2442,37 @@ void TextureCacheBase::CopyRenderTargetToTexture( } } + InvalideOverlappingTextures(dstStride, dstAddr, bytes_per_row, num_blocks_y, copy_to_vram, + copy_to_ram); + + if (OpcodeDecoder::g_record_fifo_data) + { + // Mark the memory behind this efb copy as dynamicly generated for the Fifo log + u32 address = dstAddr; + for (u32 i = 0; i < num_blocks_y; i++) + { + Core::System::GetInstance().GetFifoRecorder().UseMemory(address, bytes_per_row, + MemoryUpdate::Type::TextureMap, true); + address += dstStride; + } + } + + // Even if the copy is deferred, still compute the hash. This way if the copy is used as a texture + // in a subsequent draw before it is flushed, it will have the same hash. + if (entry) + { + const u64 hash = entry->CalculateHash(); + entry->SetHashes(hash, hash); + m_textures_by_address.emplace(dstAddr, std::move(entry)); + } +} + +void TextureCacheBase::InvalideOverlappingTextures(u32 dstStride, u32 dstAddr, u32 bytes_per_row, + u32 num_blocks_y, bool copy_to_vram, + bool copy_to_ram) +{ + const u32 covered_range = num_blocks_y * dstStride; + // Invalidate all textures, if they are either fully overwritten by our efb copy, or if they // have a different stride than our efb copy. Partly overwritten textures with the same stride // as our efb copy are marked to check them for partial texture updates. @@ -2521,27 +2535,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( } ++iter.first; } - - if (OpcodeDecoder::g_record_fifo_data) - { - // Mark the memory behind this efb copy as dynamicly generated for the Fifo log - u32 address = dstAddr; - for (u32 i = 0; i < num_blocks_y; i++) - { - Core::System::GetInstance().GetFifoRecorder().UseMemory(address, bytes_per_row, - MemoryUpdate::Type::TextureMap, true); - address += dstStride; - } - } - - // Even if the copy is deferred, still compute the hash. This way if the copy is used as a texture - // in a subsequent draw before it is flushed, it will have the same hash. - if (entry) - { - const u64 hash = entry->CalculateHash(); - entry->SetHashes(hash, hash); - m_textures_by_address.emplace(dstAddr, std::move(entry)); - } } void TextureCacheBase::FlushEFBCopies() @@ -2792,6 +2785,27 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe RcTcacheEntry& entry = iter->second; + if (g_ActiveConfig.bGraphicMods) + { + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + if (entry->is_efb_copy) + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::EFB, + entry->texture_info_name); + } + else if (entry->is_xfb_copy) + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::XFB, + entry->texture_info_name); + } + else + { + mod_manager.GetBackend().OnTextureUnload(GraphicsModSystem::TextureType::Normal, + entry->texture_info_name); + } + } + if (entry->textures_by_hash_iter != m_textures_by_hash.end()) { m_textures_by_hash.erase(entry->textures_by_hash_iter); diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 532feaca7d..9d1ccb29a2 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -168,7 +168,6 @@ struct TCacheEntry std::string texture_info_name = ""; std::vector> linked_game_texture_assets; - std::vector> linked_asset_dependencies; explicit TCacheEntry(std::unique_ptr tex, std::unique_ptr fb); @@ -345,6 +344,9 @@ private: static bool DidLinkedAssetsChange(const TCacheEntry& entry); + void InvalideOverlappingTextures(u32 dstStride, u32 dstAddr, u32 bytes_per_row, u32 num_blocks_y, + bool copy_to_vram, bool copy_to_ram); + TCacheEntry* LoadImpl(const TextureInfo& texture_info, bool force_reload); bool CreateUtilityTextures(); diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 7987887b86..27dd9732f4 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Contains.h" @@ -25,8 +27,6 @@ #include "VideoCommon/DataReader.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/GeometryShaderManager.h" -#include "VideoCommon/GraphicsModSystem/Runtime/CustomShaderCache.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/NativeVertexFormat.h" @@ -123,8 +123,8 @@ bool VertexManagerBase::Initialize() m_after_present_event = AfterPresentEvent::Register( [this](const PresentInfo& pi) { m_ticks_elapsed = pi.emulated_timestamp; }, "VertexManagerBase"); - m_index_generator.Init(); - m_custom_shader_cache = std::make_unique(); + + m_index_generator.Init(false); m_cpu_cull.Init(); return true; } @@ -137,6 +137,14 @@ u32 VertexManagerBase::GetRemainingSize() const void VertexManagerBase::AddIndices(OpcodeDecoder::Primitive primitive, u32 num_vertices) { m_index_generator.AddIndices(primitive, num_vertices); + + if (g_ActiveConfig.bGraphicMods) + { + // Tell any graphics mod backend we had more indices + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().AddIndices(primitive, num_vertices); + } } bool VertexManagerBase::AreAllVerticesCulled(VertexLoaderBase* loader, @@ -183,6 +191,13 @@ DataReader VertexManagerBase::PrepareForAdditionalData(OpcodeDecoder::Primitive // need to alloc new buffer if (m_is_flushed) [[unlikely]] { + if (g_ActiveConfig.bGraphicMods) + { + auto& system = Core::System::GetInstance(); + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().ResetIndices(); + } + if (cullall) { // This buffer isn't getting sent to the GPU. Just allocate it on the cpu. @@ -336,6 +351,7 @@ void VertexManagerBase::ResetBuffer(u32 vertex_stride) m_cur_buffer_pointer = m_cpu_vertex_buffer.data(); m_end_buffer_pointer = m_base_buffer_pointer + m_cpu_vertex_buffer.size(); m_index_generator.Start(m_cpu_index_buffer.data()); + m_last_reset_pointer = m_cur_buffer_pointer; } void VertexManagerBase::CommitBuffer(u32 num_vertices, u32 vertex_stride, u32 num_indices, @@ -547,58 +563,164 @@ void VertexManagerBase::Flush() } auto& pixel_shader_manager = system.GetPixelShaderManager(); - auto& geometry_shader_manager = system.GetGeometryShaderManager(); auto& vertex_shader_manager = system.GetVertexShaderManager(); auto& xf_state_manager = system.GetXFStateManager(); - if (g_ActiveConfig.bGraphicMods) - { - const double seconds_elapsed = - static_cast(m_ticks_elapsed) / system.GetSystemTimers().GetTicksPerSecond(); - pixel_shader_manager.constants.time_ms = seconds_elapsed * 1000; - } + const auto used_textures = UsedTextures(); CalculateNormals(VertexLoaderManager::GetCurrentVertexFormat()); - // Calculate ZSlope for zfreeze - const auto used_textures = UsedTextures(); - std::vector texture_names; - Common::SmallVector texture_units; + Common::SmallVector textures; std::array samplers; if (!m_cull_all) { - if (!g_ActiveConfig.bGraphicMods) + for (const u32 i : used_textures) { - for (const u32 i : used_textures) - { - const auto cache_entry = g_texture_cache->Load(TextureInfo::FromStage(i)); - if (!cache_entry) - continue; - const float custom_tex_scale = cache_entry->GetWidth() / float(cache_entry->native_width); - samplers[i] = TextureCacheBase::GetSamplerState( - i, custom_tex_scale, cache_entry->is_custom_tex, cache_entry->has_arbitrary_mips); - } - } - else - { - for (const u32 i : used_textures) - { - const auto cache_entry = g_texture_cache->Load(TextureInfo::FromStage(i)); - if (cache_entry) - { - if (!Common::Contains(texture_names, cache_entry->texture_info_name)) - { - texture_names.push_back(cache_entry->texture_info_name); - texture_units.push_back(i); - } + const auto cache_entry = g_texture_cache->Load(TextureInfo::FromStage(i)); + if (!cache_entry) + continue; + const float custom_tex_scale = cache_entry->GetWidth() / float(cache_entry->native_width); + samplers[i] = TextureCacheBase::GetSamplerState( + i, custom_tex_scale, cache_entry->is_custom_tex, cache_entry->has_arbitrary_mips); - const float custom_tex_scale = cache_entry->GetWidth() / float(cache_entry->native_width); - samplers[i] = TextureCacheBase::GetSamplerState( - i, custom_tex_scale, cache_entry->is_custom_tex, cache_entry->has_arbitrary_mips); + if (g_ActiveConfig.bGraphicMods) + { + GraphicsModSystem::TextureView texture; + texture.hash_name = cache_entry->texture_info_name; + texture.texture_data = cache_entry->texture.get(); + if (cache_entry->is_efb_copy) + { + texture.texture_type = GraphicsModSystem::EFB; } + else if (cache_entry->is_xfb_copy) + { + texture.texture_type = GraphicsModSystem::XFB; + } + else + { + texture.texture_type = GraphicsModSystem::Normal; + } + texture.unit = i; + textures.push_back(std::move(texture)); } } } - vertex_shader_manager.SetConstants(texture_names, xf_state_manager); + + /*Common::SmallVector, 8> lights_this_draw; + if (g_ActiveConfig.bGraphicMods) + { + // Add any lights + const auto& lights_changed = xf_state_manager.GetLightsChanged(); + if (lights_changed[0] >= 0) + { + const int istart = lights_changed[0] / 0x10; + const int iend = (lights_changed[1] + 15) / 0x10; + + for (int i = istart; i < iend; ++i) + { + const Light& light = xfmem.lights[i]; + + VertexShaderConstants::Light dstlight; + + // xfmem.light.color is packed as abgr in u8[4], so we have to swap the order + dstlight.color[0] = light.color[3]; + dstlight.color[1] = light.color[2]; + dstlight.color[2] = light.color[1]; + dstlight.color[3] = light.color[0]; + + dstlight.cosatt[0] = light.cosatt[0]; + dstlight.cosatt[1] = light.cosatt[1]; + dstlight.cosatt[2] = light.cosatt[2]; + + if (fabs(light.distatt[0]) < 0.00001f && fabs(light.distatt[1]) < 0.00001f && + fabs(light.distatt[2]) < 0.00001f) + { + // dist attenuation, make sure not equal to 0!!! + dstlight.distatt[0] = .00001f; + } + else + { + dstlight.distatt[0] = light.distatt[0]; + } + dstlight.distatt[1] = light.distatt[1]; + dstlight.distatt[2] = light.distatt[2]; + + dstlight.pos[0] = light.dpos[0]; + dstlight.pos[1] = light.dpos[1]; + dstlight.pos[2] = light.dpos[2]; + + // TODO: Hardware testing is needed to confirm that this normalization is correct + auto sanitize = [](float f) { + if (std::isnan(f)) + return 0.0f; + else if (std::isinf(f)) + return f > 0.0f ? 1.0f : -1.0f; + else + return f; + }; + double norm = double(light.ddir[0]) * double(light.ddir[0]) + + double(light.ddir[1]) * double(light.ddir[1]) + + double(light.ddir[2]) * double(light.ddir[2]); + norm = 1.0 / sqrt(norm); + dstlight.dir[0] = sanitize(static_cast(light.ddir[0] * norm)); + dstlight.dir[1] = sanitize(static_cast(light.ddir[1] * norm)); + dstlight.dir[2] = sanitize(static_cast(light.ddir[2] * norm)); + + XXH3_64bits_reset_withSeed(m_hash_state_impl->m_graphics_mod_light_hash_state, + m_hash_state_impl->m_graphics_mod_hash_seed); + // XXH3_64bits_update(m_hash_state_impl->m_graphics_mod_light_hash_state, &dstlight.color, + // sizeof(int4)); + XXH3_64bits_update(m_hash_state_impl->m_graphics_mod_light_hash_state, &dstlight.cosatt, + sizeof(float4)); + XXH3_64bits_update(m_hash_state_impl->m_graphics_mod_light_hash_state, &dstlight.distatt, + sizeof(float4)); + // XXH3_64bits_update(m_hash_state_impl->m_graphics_mod_light_hash_state, &dstlight.dir, + // sizeof(float4)); + const auto light_id = GraphicsMods::LightID( + XXH3_64bits_digest(m_hash_state_impl->m_graphics_mod_light_hash_state)); + lights_this_draw.emplace_back(light_id, i); + + if (editor.IsEnabled()) + { + GraphicsModEditor::LightData light_data; + light_data.m_id = light_id; + light_data.m_create_time = std::chrono::steady_clock::now(); + light_data.m_color = dstlight.color; + light_data.m_cosatt = dstlight.cosatt; + light_data.m_dir = dstlight.dir; + light_data.m_distatt = dstlight.distatt; + light_data.m_pos = dstlight.pos; + editor.AddLightData(std::move(light_data)); + } + } + } + }*/ + vertex_shader_manager.SetConstants(xf_state_manager); + /*if (g_ActiveConfig.bGraphicMods) + { + if (editor.IsEnabled()) + { + for (const auto& light_this_draw : lights_this_draw) + { + auto& light = vertex_shader_manager.constants.lights[light_this_draw.second]; + const auto light_id = light_this_draw.first; + bool skip = false; + GraphicsModActionData::Light light_action_data{&light.color, &light.cosatt, &light.distatt, + &light.pos, &light.dir, &skip}; + for (const auto& action : editor.GetLightActions(light_id)) + { + action->OnLight(&light_action_data); + } + if (skip) + { + light.color = {}; + light.cosatt = {}; + light.distatt = {}; + light.pos = {}; + light.dir = {}; + } + } + } + }*/ if (!bpmem.genMode.zfreeze) { // Must be done after VertexShaderManager::SetConstants() @@ -612,27 +734,6 @@ void VertexManagerBase::Flush() if (!m_cull_all) { - CustomPixelShaderContents custom_pixel_shader_contents; - std::optional custom_pixel_shader; - std::vector custom_pixel_texture_names; - std::span custom_pixel_shader_uniforms; - bool skip = false; - for (size_t i = 0; i < texture_names.size(); i++) - { - GraphicsModActionData::DrawStarted draw_started{texture_units, &skip, &custom_pixel_shader, - &custom_pixel_shader_uniforms}; - for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_names[i])) - { - action->OnDrawStarted(&draw_started); - if (custom_pixel_shader) - { - custom_pixel_shader_contents.shaders.push_back(*custom_pixel_shader); - custom_pixel_texture_names.push_back(texture_names[i]); - } - custom_pixel_shader = std::nullopt; - } - } - // Now the vertices can be flushed to the GPU. Everything following the CommitBuffer() call // must be careful to not upload any utility vertices, as the binding will be lost otherwise. const u32 num_indices = m_index_generator.GetIndexLen(); @@ -647,28 +748,30 @@ void VertexManagerBase::Flush() if (PerfQueryBase::ShouldEmulate()) g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP); - if (!skip) + UpdatePipelineConfig(); + + if (g_ActiveConfig.bGraphicMods) { - UpdatePipelineConfig(); - UpdatePipelineObject(); - if (m_current_pipeline_object) - { - const AbstractPipeline* pipeline_object = m_current_pipeline_object; - if (!custom_pixel_shader_contents.shaders.empty()) - { - if (const auto custom_pipeline = - GetCustomPipeline(custom_pixel_shader_contents, m_current_pipeline_config, - m_current_uber_pipeline_config, m_current_pipeline_object)) - { - pipeline_object = custom_pipeline; - } - } - RenderDrawCall(pixel_shader_manager, geometry_shader_manager, custom_pixel_shader_contents, - custom_pixel_shader_uniforms, m_current_primitive_type, pipeline_object); - } + GraphicsModSystem::DrawDataView draw_data; + draw_data.index_data = {m_index_generator.GetIndexDataStart(), + m_index_generator.GetIndexLen()}; + draw_data.projection_type = xfmem.projection.type; + draw_data.uid = &m_current_pipeline_config; + draw_data.vertex_data = {m_last_reset_pointer, m_index_generator.GetNumVerts()}; + draw_data.textures = std::move(textures); + draw_data.samplers = std::move(samplers); + draw_data.vertex_format = VertexLoaderManager::GetCurrentVertexFormat(); + draw_data.gpu_skinning_position_transform = vertex_shader_manager.constants.transformmatrices; + draw_data.gpu_skinning_normal_transform = vertex_shader_manager.constants.normalmatrices; + + auto& mod_manager = system.GetGraphicsModManager(); + mod_manager.GetBackend().OnDraw(draw_data, this); + } + else + { + DrawEmulatedMesh(); } - // Track the total emulated state draws INCSTAT(g_stats.this_frame.num_draw_calls); // Even if we skip the draw, emulated state should still be impacted @@ -941,7 +1044,7 @@ void VertexManagerBase::UpdatePipelineObject() void VertexManagerBase::OnConfigChange() { // Reload index generator function tables in case VS expand config changed - m_index_generator.Init(); + m_index_generator.Init(false); } void VertexManagerBase::OnDraw() @@ -1059,38 +1162,34 @@ void VertexManagerBase::OnEndFrame() InvalidatePipelineObject(); } -void VertexManagerBase::NotifyCustomShaderCacheOfHostChange(const ShaderHostConfig& host_config) +void VertexManagerBase::DrawEmulatedMesh() { - m_custom_shader_cache->SetHostConfig(host_config); - m_custom_shader_cache->Reload(); -} + auto& system = Core::System::GetInstance(); + auto& geometry_shader_manager = system.GetGeometryShaderManager(); + auto& pixel_shader_manager = system.GetPixelShaderManager(); + auto& vertex_shader_manager = system.GetVertexShaderManager(); -void VertexManagerBase::RenderDrawCall( - PixelShaderManager& pixel_shader_manager, GeometryShaderManager& geometry_shader_manager, - const CustomPixelShaderContents& custom_pixel_shader_contents, - std::span custom_pixel_shader_uniforms, PrimitiveType primitive_type, - const AbstractPipeline* current_pipeline) -{ - // Now we can upload uniforms, as nothing else will override them. - geometry_shader_manager.SetConstants(primitive_type); + UpdatePipelineObject(); + + static const auto identity_mat = Common::Matrix44::Identity(); + memcpy(vertex_shader_manager.constants.custom_transform.data(), identity_mat.data.data(), + 4 * sizeof(float4)); + + geometry_shader_manager.SetConstants(m_current_primitive_type); pixel_shader_manager.SetConstants(); - if (!custom_pixel_shader_uniforms.empty() && - pixel_shader_manager.custom_constants.data() != custom_pixel_shader_uniforms.data()) - { - pixel_shader_manager.custom_constants_dirty = true; - } - pixel_shader_manager.custom_constants = custom_pixel_shader_uniforms; UploadUniforms(); - g_gfx->SetPipeline(current_pipeline); + g_gfx->SetPipeline(m_current_pipeline_object); u32 base_vertex, base_index; CommitBuffer(m_index_generator.GetNumVerts(), VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(), m_index_generator.GetIndexLen(), &base_vertex, &base_index); - if (g_backend_info.api_type != APIType::D3D && g_ActiveConfig.UseVSForLinePointExpand() && - (primitive_type == PrimitiveType::Points || primitive_type == PrimitiveType::Lines)) + if (g_ActiveConfig.backend_info.api_type != APIType::D3D && + g_ActiveConfig.UseVSForLinePointExpand() && + (m_current_primitive_type == PrimitiveType::Points || + m_current_primitive_type == PrimitiveType::Lines)) { // VS point/line expansion puts the vertex id at gl_VertexID << 2 // That means the base vertex has to be adjusted to match @@ -1101,69 +1200,183 @@ void VertexManagerBase::RenderDrawCall( DrawCurrentBatch(base_index, m_index_generator.GetIndexLen(), base_vertex); } -const AbstractPipeline* VertexManagerBase::GetCustomPipeline( - const CustomPixelShaderContents& custom_pixel_shader_contents, - const VideoCommon::GXPipelineUid& current_pipeline_config, - const VideoCommon::GXUberPipelineUid& current_uber_pipeline_config, - const AbstractPipeline* current_pipeline) const +void VertexManagerBase::DrawEmulatedMesh(GraphicsModSystem::MaterialResource* material_resource, + const Common::Matrix44& custom_transform) { - if (current_pipeline) + auto& system = Core::System::GetInstance(); + auto& vertex_shader_manager = system.GetVertexShaderManager(); + auto& geometry_shader_manager = system.GetGeometryShaderManager(); + auto& pixel_shader_manager = system.GetPixelShaderManager(); + + memcpy(vertex_shader_manager.constants.custom_transform.data(), custom_transform.data.data(), + 4 * sizeof(float4)); + + // Now we can upload uniforms, as nothing else will override them. + geometry_shader_manager.SetConstants(m_current_primitive_type); + pixel_shader_manager.SetConstants(); + if (material_resource && !material_resource->pixel_uniform_data.empty()) { - if (!custom_pixel_shader_contents.shaders.empty()) + pixel_shader_manager.custom_constants = material_resource->pixel_uniform_data; + pixel_shader_manager.custom_constants_dirty = true; + } + UploadUniforms(); + + if (material_resource) + { + g_gfx->SetPipeline(material_resource->pipeline); + + const std::size_t custom_sampler_index_offset = 8; + for (std::size_t i = 0; i < material_resource->textures.size(); i++) { - CustomShaderInstance custom_shaders; - custom_shaders.pixel_contents = custom_pixel_shader_contents; - switch (g_ActiveConfig.iShaderCompilationMode) - { - case ShaderCompilationMode::Synchronous: - case ShaderCompilationMode::AsynchronousSkipRendering: - { - if (auto pipeline = m_custom_shader_cache->GetPipelineAsync( - current_pipeline_config, custom_shaders, current_pipeline->m_config)) - { - return *pipeline; - } - } - break; - case ShaderCompilationMode::SynchronousUberShaders: - { - // D3D has issues compiling large custom ubershaders - // use specialized shaders instead - if (g_backend_info.api_type == APIType::D3D) - { - if (auto pipeline = m_custom_shader_cache->GetPipelineAsync( - current_pipeline_config, custom_shaders, current_pipeline->m_config)) - { - return *pipeline; - } - } - else - { - if (auto pipeline = m_custom_shader_cache->GetPipelineAsync( - current_uber_pipeline_config, custom_shaders, current_pipeline->m_config)) - { - return *pipeline; - } - } - } - break; - case ShaderCompilationMode::AsynchronousUberShaders: - { - if (auto pipeline = m_custom_shader_cache->GetPipelineAsync( - current_pipeline_config, custom_shaders, current_pipeline->m_config)) - { - return *pipeline; - } - else if (auto uber_pipeline = m_custom_shader_cache->GetPipelineAsync( - current_uber_pipeline_config, custom_shaders, current_pipeline->m_config)) - { - return *uber_pipeline; - } - } - break; - }; + auto& texture_resource = material_resource->textures[i]; + if (texture_resource.sampler == nullptr || texture_resource.texture == nullptr) [[unlikely]] + continue; + g_gfx->SetTexture(texture_resource.sampler_index + custom_sampler_index_offset, + texture_resource.texture); + g_gfx->SetSamplerState(texture_resource.sampler_index + custom_sampler_index_offset, + *texture_resource.sampler); } } + else + { + UpdatePipelineObject(); + g_gfx->SetPipeline(m_current_pipeline_object); + } - return nullptr; + u32 base_vertex, base_index; + CommitBuffer(m_index_generator.GetNumVerts(), + VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(), + m_index_generator.GetIndexLen(), &base_vertex, &base_index); + + if (g_backend_info.api_type != APIType::D3D && g_ActiveConfig.UseVSForLinePointExpand() && + (m_current_primitive_type == PrimitiveType::Points || + m_current_primitive_type == PrimitiveType::Lines)) + { + // VS point/line expansion puts the vertex id at gl_VertexID << 2 + // That means the base vertex has to be adjusted to match + // (The shader adds this after shifting right on D3D, so no need to do this) + base_vertex <<= 2; + } + + DrawCurrentBatch(base_index, m_index_generator.GetIndexLen(), base_vertex); + + if (material_resource && material_resource->next) + { + RedrawWithNewMaterial(base_vertex, base_index, m_index_generator.GetIndexLen(), + m_current_primitive_type, material_resource->next); + } +} + +void VertexManagerBase::DrawCustomMesh(GraphicsModSystem::MeshResource* mesh_resource, + const Common::Matrix44& custom_transform, + bool ignore_mesh_transform) +{ + auto& system = Core::System::GetInstance(); + auto& vertex_shader_manager = system.GetVertexShaderManager(); + auto& geometry_shader_manager = system.GetGeometryShaderManager(); + auto& pixel_shader_manager = system.GetPixelShaderManager(); + + for (const auto& mesh_chunk : mesh_resource->mesh_chunks) + { + // TODO: draw with a generic material? + if (!mesh_chunk.material) [[unlikely]] + continue; + + if (!mesh_chunk.material->pipeline || !mesh_chunk.material->pipeline->m_config.vertex_shader || + !mesh_chunk.material->pipeline->m_config.pixel_shader) [[unlikely]] + continue; + + vertex_shader_manager.SetVertexFormat(mesh_chunk.components_available, + mesh_chunk.vertex_format->GetVertexDeclaration()); + + Common::Matrix44 computed_transform; + computed_transform = Common::Matrix44::Translate(mesh_resource->pivot_point) * custom_transform; + if (!ignore_mesh_transform) + { + computed_transform *= mesh_chunk.transform; + } + memcpy(vertex_shader_manager.constants.custom_transform.data(), computed_transform.data.data(), + 4 * sizeof(float4)); + + // Now we can upload uniforms, as nothing else will override them. + geometry_shader_manager.SetConstants(mesh_chunk.primitive_type); + pixel_shader_manager.SetConstants(); + if (!mesh_chunk.material->pixel_uniform_data.empty()) + { + pixel_shader_manager.custom_constants_dirty = true; + pixel_shader_manager.custom_constants = mesh_chunk.material->pixel_uniform_data; + } + UploadUniforms(); + + g_gfx->SetPipeline(mesh_chunk.material->pipeline); + + const std::size_t custom_sampler_index_offset = 8; + for (std::size_t i = 0; i < mesh_chunk.material->textures.size(); i++) + { + auto& texture_resource = mesh_chunk.material->textures[i]; + if (texture_resource.sampler == nullptr || texture_resource.texture == nullptr) [[unlikely]] + continue; + g_gfx->SetTexture(texture_resource.sampler_index + custom_sampler_index_offset, + texture_resource.texture); + g_gfx->SetSamplerState(texture_resource.sampler_index + custom_sampler_index_offset, + *texture_resource.sampler); + } + + u32 base_vertex, base_index; + UploadUtilityVertices( + mesh_chunk.vertex_data.data(), mesh_chunk.vertex_stride, + static_cast(mesh_chunk.vertex_data.size()), mesh_chunk.index_data.data(), + static_cast(mesh_chunk.index_data.size()), &base_vertex, &base_index); + g_gfx->DrawIndexed(base_index, static_cast(mesh_chunk.index_data.size()), base_vertex); + + if (mesh_chunk.material->next) + { + RedrawWithNewMaterial(base_vertex, base_index, static_cast(mesh_chunk.index_data.size()), + mesh_chunk.primitive_type, mesh_chunk.material->next); + } + } +} + +void VertexManagerBase::RedrawWithNewMaterial( + u32 base_vertex, u32 base_index, u32 index_size, PrimitiveType primitive_type, + GraphicsModSystem::MaterialResource* material_resource) +{ + if (!material_resource) [[unlikely]] + return; + + auto& system = Core::System::GetInstance(); + auto& geometry_shader_manager = system.GetGeometryShaderManager(); + auto& pixel_shader_manager = system.GetPixelShaderManager(); + + // Now we can upload uniforms, as nothing else will override them. + geometry_shader_manager.SetConstants(primitive_type); + pixel_shader_manager.SetConstants(); + if (!material_resource->pixel_uniform_data.empty()) + { + pixel_shader_manager.custom_constants = material_resource->pixel_uniform_data; + pixel_shader_manager.custom_constants_dirty = true; + } + UploadUniforms(); + + g_gfx->SetPipeline(material_resource->pipeline); + + const std::size_t custom_sampler_index_offset = 8; + for (std::size_t i = 0; i < material_resource->textures.size(); i++) + { + auto& texture_resource = material_resource->textures[i]; + if (texture_resource.sampler == nullptr || texture_resource.texture == nullptr) [[unlikely]] + continue; + g_gfx->SetTexture(texture_resource.sampler_index + custom_sampler_index_offset, + texture_resource.texture); + g_gfx->SetSamplerState(texture_resource.sampler_index + custom_sampler_index_offset, + *texture_resource.sampler); + } + + DrawCurrentBatch(base_index, index_size, base_vertex); + + if (material_resource->next) + { + RedrawWithNewMaterial(base_vertex, base_index, index_size, primitive_type, + material_resource->next); + } } diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 715dbee692..a22d73c5fa 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -3,12 +3,14 @@ #pragma once +#include #include #include #include "Common/BitSet.h" #include "Common/CommonTypes.h" #include "Common/MathUtil.h" +#include "Common/Matrix.h" #include "VideoCommon/CPUCull.h" #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/RenderState.h" @@ -16,7 +18,6 @@ #include "VideoCommon/VideoEvents.h" struct CustomPixelShaderContents; -class CustomShaderCache; class DataReader; class GeometryShaderManager; class NativeVertexFormat; @@ -24,6 +25,12 @@ class PixelShaderManager; class PointerWrap; struct PortableVertexDeclaration; +namespace GraphicsModSystem +{ +struct MaterialResource; +struct MeshResource; +} // namespace GraphicsModSystem + struct Slope { float dfdx; @@ -134,7 +141,6 @@ public: m_current_pipeline_object = nullptr; m_pipeline_config_changed = true; } - void NotifyCustomShaderCacheOfHostChange(const ShaderHostConfig& host_config); // Utility pipeline drawing (e.g. EFB copies, post-processing, UI). virtual void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size); @@ -171,7 +177,22 @@ public: // Call at the end of a frame. void OnEndFrame(); + // Draws the normal mesh sourced from emulation + void DrawEmulatedMesh(); + + // Draws the normal mesh sourced from emulation, with a custom shader and/or transform + void DrawEmulatedMesh(GraphicsModSystem::MaterialResource* material_resource, + const Common::Matrix44& custom_transform); + + // Draw a custom mesh sourced from a mod, with a custom shader and custom vertex information + void DrawCustomMesh(GraphicsModSystem::MeshResource* mesh_resource, + const Common::Matrix44& custom_transform, bool ignore_mesh_transform); + protected: + void RedrawWithNewMaterial(u32 base_vertex, u32 base_index, u32 index_size, + PrimitiveType primitive_type, + GraphicsModSystem::MaterialResource* material_resource); + // When utility uniforms are used, the GX uniforms need to be re-written afterwards. static void InvalidateConstants(); @@ -199,6 +220,7 @@ protected: u8* m_cur_buffer_pointer = nullptr; u8* m_base_buffer_pointer = nullptr; u8* m_end_buffer_pointer = nullptr; + u8* m_last_reset_pointer = nullptr; // Alternative buffers in CPU memory for primitives we are going to discard. std::vector m_cpu_vertex_buffer; @@ -223,20 +245,9 @@ private: // Minimum number of draws per command buffer when attempting to preempt a readback operation. static constexpr u32 MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK = 10; - void RenderDrawCall(PixelShaderManager& pixel_shader_manager, - GeometryShaderManager& geometry_shader_manager, - const CustomPixelShaderContents& custom_pixel_shader_contents, - std::span custom_pixel_shader_uniforms, PrimitiveType primitive_type, - const AbstractPipeline* current_pipeline); void UpdatePipelineConfig(); void UpdatePipelineObject(); - const AbstractPipeline* - GetCustomPipeline(const CustomPixelShaderContents& custom_pixel_shader_contents, - const VideoCommon::GXPipelineUid& current_pipeline_config, - const VideoCommon::GXUberPipelineUid& current_uber_pipeline_confi, - const AbstractPipeline* current_pipeline) const; - bool m_is_flushed = true; FlushStatistics m_flush_statistics = {}; @@ -248,7 +259,6 @@ private: std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; - std::unique_ptr m_custom_shader_cache; u64 m_ticks_elapsed = 0; Common::EventHook m_frame_end_event; diff --git a/Source/Core/VideoCommon/VertexShaderManager.cpp b/Source/Core/VideoCommon/VertexShaderManager.cpp index 976cff00f6..b1ae563c08 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.cpp +++ b/Source/Core/VideoCommon/VertexShaderManager.cpp @@ -12,13 +12,12 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/Matrix.h" +#include "Core/System.h" #include "VideoCommon/BPFunctions.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FreeLookCamera.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h" -#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoCommon.h" @@ -29,7 +28,6 @@ void VertexShaderManager::Init() { // Initialize state tracking variables - m_projection_graphics_mod_change = false; constants = {}; @@ -156,8 +154,7 @@ bool VertexShaderManager::UseVertexDepthRange() // Syncs the shader constant buffers with xfmem // TODO: A cleaner way to control the matrices without making a mess in the parameters field -void VertexShaderManager::SetConstants(const std::vector& textures, - XFStateManager& xf_state_manager) +void VertexShaderManager::SetConstants(XFStateManager& xf_state_manager) { if (constants.missing_color_hex != g_ActiveConfig.iMissingColorValue) { @@ -387,38 +384,12 @@ void VertexShaderManager::SetConstants(const std::vector& textures, g_stats.AddScissorRect(); } - std::vector projection_actions; - if (g_ActiveConfig.bGraphicMods) - { - for (const auto& action : g_graphics_mod_manager->GetProjectionActions(xfmem.projection.type)) - { - projection_actions.push_back(action); - } - - for (const auto& texture : textures) - { - for (const auto& action : - g_graphics_mod_manager->GetProjectionTextureActions(xfmem.projection.type, texture)) - { - projection_actions.push_back(action); - } - } - } - - if (xf_state_manager.DidProjectionChange() || g_freelook_camera.GetController()->IsDirty() || - !projection_actions.empty() || m_projection_graphics_mod_change) + if (xf_state_manager.DidProjectionChange() || g_freelook_camera.GetController()->IsDirty()) { xf_state_manager.ResetProjection(); - m_projection_graphics_mod_change = !projection_actions.empty(); auto corrected_matrix = LoadProjectionMatrix(); - GraphicsModActionData::Projection projection{&corrected_matrix}; - for (const auto& action : projection_actions) - { - action->OnProjection(&projection); - } - memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4)); dirty = true; } diff --git a/Source/Core/VideoCommon/VertexShaderManager.h b/Source/Core/VideoCommon/VertexShaderManager.h index 9678aae58f..4ba4b6216b 100644 --- a/Source/Core/VideoCommon/VertexShaderManager.h +++ b/Source/Core/VideoCommon/VertexShaderManager.h @@ -5,7 +5,6 @@ #include #include -#include #include "Common/BitSet.h" #include "Common/CommonTypes.h" @@ -26,7 +25,7 @@ public: // constant management void SetProjectionMatrix(XFStateManager& xf_state_manager); - void SetConstants(const std::vector& textures, XFStateManager& xf_state_manager); + void SetConstants(XFStateManager& xf_state_manager); // data: 3 floats representing the X, Y and Z vertex model coordinates and the posmatrix index. // out: 4 floats which will be initialized with the corresponding clip space coordinates @@ -82,7 +81,5 @@ private: alignas(16) std::array m_projection_matrix; // track changes - bool m_projection_graphics_mod_change = false; - Common::Matrix44 LoadProjectionMatrix(); }; diff --git a/Source/Core/VideoCommon/VideoBackendBase.cpp b/Source/Core/VideoCommon/VideoBackendBase.cpp index 8150120610..87a12fbb05 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.cpp +++ b/Source/Core/VideoCommon/VideoBackendBase.cpp @@ -343,6 +343,8 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, memset(reinterpret_cast(&g_preprocess_cp_state), 0, sizeof(g_preprocess_cp_state)); s_tex_mem.fill(0); + auto& system = Core::System::GetInstance(); + // do not initialize again for the config window m_initialized = true; @@ -359,21 +361,19 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr gfx, g_frame_dumper = std::make_unique(); g_framebuffer_manager = std::make_unique(); g_shader_cache = std::make_unique(); - g_graphics_mod_manager = std::make_unique(); g_widescreen = std::make_unique(); if (!g_vertex_manager->Initialize() || !g_shader_cache->Initialize() || !g_perf_query->Initialize() || !g_presenter->Initialize() || !g_framebuffer_manager->Initialize() || !g_texture_cache->Initialize() || (g_backend_info.bSupportsBBox && !g_bounding_box->Initialize()) || - !g_graphics_mod_manager->Initialize()) + !system.GetGraphicsModManager().Initialize()) { PanicAlertFmtT("Failed to initialize renderer classes"); Shutdown(); return false; } - auto& system = Core::System::GetInstance(); auto& command_processor = system.GetCommandProcessor(); command_processor.Init(); system.GetFifo().Init(); @@ -412,7 +412,6 @@ void VideoBackendBase::ShutdownShared() g_bounding_box.reset(); g_perf_query.reset(); - g_graphics_mod_manager.reset(); g_texture_cache.reset(); g_framebuffer_manager.reset(); g_shader_cache.reset();