VideoCommon: update emulation classes for graphics mod 2.0

This commit is contained in:
iwubcode 2023-07-25 18:14:38 -05:00
parent 61644f4de5
commit f3e6c1e74b
7 changed files with 535 additions and 329 deletions

View file

@ -4,6 +4,7 @@
#include "VideoCommon/TextureCacheBase.h"
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstring>
#include <memory>
@ -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<VideoCommon::CachedAsset<VideoCommon::CustomAsset>> 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);

View file

@ -168,7 +168,6 @@ struct TCacheEntry
std::string texture_info_name = "";
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> linked_game_texture_assets;
std::vector<VideoCommon::CachedAsset<VideoCommon::CustomAsset>> linked_asset_dependencies;
explicit TCacheEntry(std::unique_ptr<AbstractTexture> tex,
std::unique_ptr<AbstractFramebuffer> 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();

View file

@ -7,6 +7,8 @@
#include <cmath>
#include <memory>
#include <xxhash.h>
#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<CustomShaderCache>();
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<double>(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<std::string> texture_names;
Common::SmallVector<u32, 8> texture_units;
Common::SmallVector<GraphicsModSystem::TextureView, 8> textures;
std::array<SamplerState, 8> 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<std::pair<GraphicsMods::LightID, int>, 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<float>(light.ddir[0] * norm));
dstlight.dir[1] = sanitize(static_cast<float>(light.ddir[1] * norm));
dstlight.dir[2] = sanitize(static_cast<float>(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<CustomPixelShader> custom_pixel_shader;
std::vector<std::string> custom_pixel_texture_names;
std::span<u8> 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<u8> 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<u32>(mesh_chunk.vertex_data.size()), mesh_chunk.index_data.data(),
static_cast<u32>(mesh_chunk.index_data.size()), &base_vertex, &base_index);
g_gfx->DrawIndexed(base_index, static_cast<u32>(mesh_chunk.index_data.size()), base_vertex);
if (mesh_chunk.material->next)
{
RedrawWithNewMaterial(base_vertex, base_index, static_cast<u32>(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);
}
}

View file

@ -3,12 +3,14 @@
#pragma once
#include <map>
#include <memory>
#include <vector>
#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<u8> 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<u8> 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<u32> m_scheduled_command_buffer_kicks;
bool m_allow_background_execution = true;
std::unique_ptr<CustomShaderCache> m_custom_shader_cache;
u64 m_ticks_elapsed = 0;
Common::EventHook m_frame_end_event;

View file

@ -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<std::string>& 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<std::string>& textures,
g_stats.AddScissorRect();
}
std::vector<GraphicsModAction*> 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;
}

View file

@ -5,7 +5,6 @@
#include <array>
#include <string>
#include <vector>
#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<std::string>& 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<float, 16> m_projection_matrix;
// track changes
bool m_projection_graphics_mod_change = false;
Common::Matrix44 LoadProjectionMatrix();
};

View file

@ -343,6 +343,8 @@ bool VideoBackendBase::InitializeShared(std::unique_ptr<AbstractGfx> gfx,
memset(reinterpret_cast<u8*>(&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<AbstractGfx> gfx,
g_frame_dumper = std::make_unique<FrameDumper>();
g_framebuffer_manager = std::make_unique<FramebufferManager>();
g_shader_cache = std::make_unique<VideoCommon::ShaderCache>();
g_graphics_mod_manager = std::make_unique<GraphicsModManager>();
g_widescreen = std::make_unique<WidescreenManager>();
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();