From ae758f489a88b4e6be2a72a6423c6d931bdec3db Mon Sep 17 00:00:00 2001 From: iwubcode Date: Sun, 17 Dec 2023 23:37:23 -0600 Subject: [PATCH] VideoCommon: update custom pipeline action to support a variety of texture samplers (2D, 2D array, and cube maps) --- .../Runtime/Actions/CustomPipelineAction.cpp | 348 ++++++++++-------- .../Runtime/Actions/CustomPipelineAction.h | 17 +- .../Runtime/GraphicsModActionData.h | 3 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 13 +- 4 files changed, 220 insertions(+), 161 deletions(-) diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp index 0a51bb995e..f457300847 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp @@ -11,6 +11,7 @@ #include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/StringUtil.h" +#include "Common/VariantUtil.h" #include "Core/System.h" #include "VideoCommon/AbstractGfx.h" @@ -175,20 +176,6 @@ std::vector GlobalConflicts(std::string_view source) return global_result; } -void WriteDefines(ShaderCode* out, const std::vector& texture_code_names, - u32 texture_unit) -{ - for (std::size_t i = 0; i < texture_code_names.size(); i++) - { - const auto& code_name = texture_code_names[i]; - out->Write("#define {}_UNIT_{{0}} {}\n", code_name, texture_unit); - out->Write( - "#define {0}_COORD_{{0}} float3(data.texcoord[data.texmap_to_texcoord_index[{1}]].xy, " - "{2})\n", - code_name, texture_unit, i + 1); - } -} - } // namespace std::unique_ptr @@ -267,186 +254,251 @@ void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted* dra if (!draw_started->custom_pixel_shader) [[unlikely]] return; - if (!m_valid) + if (!draw_started->material_uniform_buffer) [[unlikely]] return; if (m_passes.empty()) [[unlikely]] return; - // For now assume a single pass - auto& pass = m_passes[0]; - - if (!pass.m_pixel_shader.m_asset) [[unlikely]] - return; - - const auto shader_data = pass.m_pixel_shader.m_asset->GetData(); - if (shader_data) - { - if (m_last_generated_shader_code.GetBuffer().empty()) - { - // Calculate shader details - std::string color_shader_data = - ReplaceAll(shader_data->m_shader_source, "custom_main", CUSTOM_PIXELSHADER_COLOR_FUNC); - const auto global_conflicts = GlobalConflicts(color_shader_data); - color_shader_data = ReplaceAll(color_shader_data, "\r\n", "\n"); - color_shader_data = ReplaceAll(color_shader_data, "{", "{{"); - color_shader_data = ReplaceAll(color_shader_data, "}", "}}"); - // First replace global conflicts with dummy strings - // This avoids the problem where a shorter word - // is in a longer word, ex two functions: 'execute' and 'execute_fast' - for (std::size_t i = 0; i < global_conflicts.size(); i++) - { - const std::string& identifier = global_conflicts[i]; - color_shader_data = - ReplaceAll(color_shader_data, identifier, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i)); - } - // Now replace the temporaries with the actual value - for (std::size_t i = 0; i < global_conflicts.size(); i++) - { - const std::string& identifier = global_conflicts[i]; - color_shader_data = ReplaceAll(color_shader_data, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i), - fmt::format("{}_{{0}}", identifier)); - } - - for (const auto& texture_code_name : m_texture_code_names) - { - color_shader_data = - ReplaceAll(color_shader_data, fmt::format("{}_COORD", texture_code_name), - fmt::format("{}_COORD_{{0}}", texture_code_name)); - color_shader_data = ReplaceAll(color_shader_data, fmt::format("{}_UNIT", texture_code_name), - fmt::format("{}_UNIT_{{0}}", texture_code_name)); - } - - WriteDefines(&m_last_generated_shader_code, m_texture_code_names, draw_started->texture_unit); - m_last_generated_shader_code.Write("{}", color_shader_data); - } - CustomPixelShader custom_pixel_shader; - custom_pixel_shader.custom_shader = m_last_generated_shader_code.GetBuffer(); - *draw_started->custom_pixel_shader = custom_pixel_shader; - } -} - -void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate* create) -{ - if (!create->custom_textures) [[unlikely]] - return; - - if (!create->additional_dependencies) [[unlikely]] - return; - - if (m_passes_config.empty()) [[unlikely]] - return; - - if (m_passes.empty()) [[unlikely]] - return; - - m_valid = true; auto& loader = Core::System::GetInstance().GetCustomAssetLoader(); // For now assume a single pass const auto& pass_config = m_passes_config[0]; auto& pass = m_passes[0]; - if (!pass.m_pixel_material.m_asset) + if (!pass.m_pixel_material.m_asset || + pass_config.m_pixel_material_asset != pass.m_pixel_material.m_asset->GetAssetId()) { pass.m_pixel_material.m_asset = loader.LoadMaterial(pass_config.m_pixel_material_asset, m_library); - pass.m_pixel_material.m_cached_write_time = pass.m_pixel_material.m_asset->GetLastLoadedTime(); } - create->additional_dependencies->push_back(VideoCommon::CachedAsset{ - pass.m_pixel_material.m_asset, pass.m_pixel_material.m_asset->GetLastLoadedTime()}); const auto material_data = pass.m_pixel_material.m_asset->GetData(); if (!material_data) - return; - - if (!pass.m_pixel_shader.m_asset || pass.m_pixel_material.m_asset->GetLastLoadedTime() > - pass.m_pixel_material.m_cached_write_time) { - m_last_generated_shader_code = ShaderCode{}; + return; + } + + std::size_t max_material_data_size = 0; + if (pass.m_pixel_material.m_asset->GetLastLoadedTime() > + pass.m_pixel_material.m_cached_write_time) + { + m_last_generated_material_code = ShaderCode{}; + pass.m_pixel_material.m_cached_write_time = pass.m_pixel_material.m_asset->GetLastLoadedTime(); + std::size_t texture_count = 0; + for (const auto& property : material_data->properties) + { + max_material_data_size += VideoCommon::MaterialProperty::GetMemorySize(property); + VideoCommon::MaterialProperty::WriteAsShaderCode(m_last_generated_material_code, property); + if (auto* texture_asset_id = + std::get_if(&property.m_value)) + { + texture_count++; + } + } + m_material_data.resize(max_material_data_size); + pass.m_game_textures.resize(texture_count); + } + + if (!pass.m_pixel_shader.m_asset || + pass.m_pixel_shader.m_asset->GetLastLoadedTime() > pass.m_pixel_shader.m_cached_write_time || + material_data->shader_asset != pass.m_pixel_shader.m_asset->GetAssetId()) + { pass.m_pixel_shader.m_asset = loader.LoadPixelShader(material_data->shader_asset, m_library); pass.m_pixel_shader.m_cached_write_time = pass.m_pixel_shader.m_asset->GetLastLoadedTime(); + + m_last_generated_shader_code = ShaderCode{}; } - create->additional_dependencies->push_back(VideoCommon::CachedAsset{ - pass.m_pixel_shader.m_asset, pass.m_pixel_shader.m_asset->GetLastLoadedTime()}); const auto shader_data = pass.m_pixel_shader.m_asset->GetData(); if (!shader_data) { - m_valid = false; return; } - m_texture_code_names.clear(); - std::vector> game_assets; - for (const auto& property : material_data->properties) + if (shader_data->m_properties.size() != material_data->properties.size()) { + return; + } + + u8* material_buffer = m_material_data.data(); + u32 sampler_index = 8; + for (std::size_t index = 0; index < material_data->properties.size(); index++) + { + auto& property = material_data->properties[index]; const auto shader_it = shader_data->m_properties.find(property.m_code_name); if (shader_it == shader_data->m_properties.end()) { ERROR_LOG_FMT(VIDEO, - "Custom pipeline for texture '{}' has material asset '{}' that uses a " + "Custom pipeline, has material asset '{}' that uses a " "code name of '{}' but that can't be found on shader asset '{}'!", - create->texture_name, pass.m_pixel_material.m_asset->GetAssetId(), - property.m_code_name, pass.m_pixel_shader.m_asset->GetAssetId()); - m_valid = false; + pass.m_pixel_material.m_asset->GetAssetId(), property.m_code_name, + pass.m_pixel_shader.m_asset->GetAssetId()); return; } - if (auto* value = std::get_if(&property.m_value)) + if (auto* texture_asset_id = + std::get_if(&property.m_value)) { - auto asset = loader.LoadGameTexture(*value, m_library); - if (asset) + if (*texture_asset_id != "") { - const auto loaded_time = asset->GetLastLoadedTime(); - game_assets.push_back( - VideoCommon::CachedAsset{std::move(asset), loaded_time}); - m_texture_code_names.push_back(property.m_code_name); + auto asset = loader.LoadGameTexture(*texture_asset_id, m_library); + if (!asset) + { + return; + } + + auto& texture_asset = pass.m_game_textures[index]; + if (!texture_asset || + texture_asset->m_cached_asset.m_asset->GetLastLoadedTime() > + texture_asset->m_cached_asset.m_cached_write_time || + *texture_asset_id != texture_asset->m_cached_asset.m_asset->GetAssetId()) + { + if (!texture_asset) + { + texture_asset = PipelinePass::CachedTextureAsset{}; + } + const auto loaded_time = asset->GetLastLoadedTime(); + texture_asset->m_cached_asset = VideoCommon::CachedAsset{ + std::move(asset), loaded_time}; + texture_asset->m_texture.reset(); + + if (std::holds_alternative( + shader_it->second.m_default)) + { + texture_asset->m_sampler_code = + fmt::format("SAMPLER_BINDING({}) uniform sampler2D samp_{};\n", sampler_index, + property.m_code_name); + texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name); + } + else if (std::holds_alternative( + shader_it->second.m_default)) + { + texture_asset->m_sampler_code = + fmt::format("SAMPLER_BINDING({}) uniform sampler2DArray samp_{};\n", sampler_index, + property.m_code_name); + texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name); + } + else if (std::holds_alternative( + shader_it->second.m_default)) + { + texture_asset->m_sampler_code = + fmt::format("SAMPLER_BINDING({}) uniform samplerCube samp_{};\n", sampler_index, + property.m_code_name); + texture_asset->m_define_code = fmt::format("#define HAS_{} 1\n", property.m_code_name); + } + } + + const auto texture_data = texture_asset->m_cached_asset.m_asset->GetData(); + if (!texture_data) + { + return; + } + + if (texture_asset->m_texture) + { + g_gfx->SetTexture(sampler_index, texture_asset->m_texture.get()); + g_gfx->SetSamplerState(sampler_index, texture_data->m_sampler); + } + else + { + AbstractTextureType texture_usage = AbstractTextureType::Texture_2DArray; + if (std::holds_alternative( + shader_it->second.m_default)) + { + texture_usage = AbstractTextureType::Texture_CubeMap; + } + else if (std::holds_alternative( + shader_it->second.m_default)) + { + texture_usage = AbstractTextureType::Texture_2D; + } + + if (texture_data->m_texture.m_slices.empty() || + texture_data->m_texture.m_slices[0].m_levels.empty()) + { + return; + } + + auto& first_slice = texture_data->m_texture.m_slices[0]; + const TextureConfig texture_config( + first_slice.m_levels[0].width, first_slice.m_levels[0].height, + static_cast(first_slice.m_levels.size()), + static_cast(texture_data->m_texture.m_slices.size()), 1, + first_slice.m_levels[0].format, 0, texture_usage); + texture_asset->m_texture = g_gfx->CreateTexture( + texture_config, fmt::format("Custom shader texture '{}'", property.m_code_name)); + for (std::size_t slice_index = 0; slice_index < texture_data->m_texture.m_slices.size(); + slice_index++) + { + auto& slice = texture_data->m_texture.m_slices[slice_index]; + for (u32 level_index = 0; level_index < static_cast(slice.m_levels.size()); + ++level_index) + { + auto& level = slice.m_levels[level_index]; + texture_asset->m_texture->Load(level_index, level.width, level.height, + level.row_length, level.data.data(), level.data.size(), + static_cast(slice_index)); + } + } + } + + sampler_index++; } } + else + { + VideoCommon::MaterialProperty::WriteToMemory(material_buffer, property); + } } - // Note: we swap here instead of doing a clear + append of the member - // variable so that any loaded assets from previous iterations - // won't be let go - std::swap(pass.m_game_textures, game_assets); - for (auto& game_texture : pass.m_game_textures) + if (m_last_generated_shader_code.GetBuffer().empty()) { - if (game_texture.m_asset) + // Calculate shader details + std::string color_shader_data = + ReplaceAll(shader_data->m_shader_source, "custom_main", CUSTOM_PIXELSHADER_COLOR_FUNC); + const auto global_conflicts = GlobalConflicts(color_shader_data); + color_shader_data = ReplaceAll(color_shader_data, "\r\n", "\n"); + color_shader_data = ReplaceAll(color_shader_data, "{", "{{"); + color_shader_data = ReplaceAll(color_shader_data, "}", "}}"); + // First replace global conflicts with dummy strings + // This avoids the problem where a shorter word + // is in a longer word, ex two functions: 'execute' and 'execute_fast' + for (std::size_t i = 0; i < global_conflicts.size(); i++) { - auto data = game_texture.m_asset->GetData(); - if (data) - { - if (data->m_texture.m_slices.empty() || data->m_texture.m_slices[0].m_levels.empty()) - { - ERROR_LOG_FMT( - VIDEO, - "Custom pipeline for texture '{}' has asset '{}' that does not have any texture data", - create->texture_name, game_texture.m_asset->GetAssetId()); - m_valid = false; - } - else if (create->texture_width != data->m_texture.m_slices[0].m_levels[0].width || - create->texture_height != data->m_texture.m_slices[0].m_levels[0].height) - { - ERROR_LOG_FMT(VIDEO, - "Custom pipeline for texture '{}' has asset '{}' that does not match " - "the width/height of the texture loaded. Texture {}x{} vs asset {}x{}", - create->texture_name, game_texture.m_asset->GetAssetId(), - create->texture_width, create->texture_height, - data->m_texture.m_slices[0].m_levels[0].width, - data->m_texture.m_slices[0].m_levels[0].height); - m_valid = false; - } - } - else - { - m_valid = false; - } + const std::string& identifier = global_conflicts[i]; + color_shader_data = + ReplaceAll(color_shader_data, identifier, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i)); } + // Now replace the temporaries with the actual value + for (std::size_t i = 0; i < global_conflicts.size(); i++) + { + const std::string& identifier = global_conflicts[i]; + color_shader_data = ReplaceAll(color_shader_data, fmt::format("_{0}_DOLPHIN_TEMP_{0}_", i), + fmt::format("{}_{{0}}", identifier)); + } + + for (const auto& game_texture : pass.m_game_textures) + { + if (!game_texture) + continue; + + m_last_generated_shader_code.Write("{}", game_texture->m_sampler_code); + m_last_generated_shader_code.Write("{}", game_texture->m_define_code); + } + + for (std::size_t i = 0; i < draw_started->texture_units.size(); i++) + { + const auto& texture_unit = draw_started->texture_units[i]; + m_last_generated_shader_code.Write( + "#define TEX_COORD{} data.texcoord[data.texmap_to_texcoord_index[{}]].xy\n", i, + texture_unit); + } + m_last_generated_shader_code.Write("{}", color_shader_data); } - - // TODO: compare game textures and shader requirements - - create->custom_textures->insert(create->custom_textures->end(), pass.m_game_textures.begin(), - pass.m_game_textures.end()); + CustomPixelShader custom_pixel_shader; + custom_pixel_shader.custom_shader = m_last_generated_shader_code.GetBuffer(); + custom_pixel_shader.material_uniform_block = m_last_generated_material_code.GetBuffer(); + *draw_started->custom_pixel_shader = custom_pixel_shader; + *draw_started->material_uniform_buffer = m_material_data; } diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h index 4760da3124..d609f065fb 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -33,7 +34,6 @@ public: std::vector pass_descriptions); ~CustomPipelineAction(); void OnDrawStarted(GraphicsModActionData::DrawStarted*) override; - void OnTextureCreate(GraphicsModActionData::TextureCreate*) override; private: std::shared_ptr m_library; @@ -42,13 +42,20 @@ private: { VideoCommon::CachedAsset m_pixel_material; VideoCommon::CachedAsset m_pixel_shader; - std::vector> m_game_textures; + + struct CachedTextureAsset + { + VideoCommon::CachedAsset m_cached_asset; + std::unique_ptr m_texture; + std::string m_sampler_code; + std::string m_define_code; + }; + std::vector> m_game_textures; }; std::vector m_passes; ShaderCode m_last_generated_shader_code; + ShaderCode m_last_generated_material_code; - bool m_valid = true; - - std::vector m_texture_code_names; + std::vector m_material_data; }; diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h index 1b9115e6d5..cecd0ce94f 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h @@ -11,6 +11,7 @@ #include "Common/CommonTypes.h" #include "Common/Matrix.h" +#include "Common/SmallVector.h" #include "VideoCommon/Assets/TextureAsset.h" #include "VideoCommon/PixelShaderGen.h" @@ -18,7 +19,7 @@ namespace GraphicsModActionData { struct DrawStarted { - u32 texture_unit; + const Common::SmallVector& texture_units; bool* skip; std::optional* custom_pixel_shader; std::span* material_uniform_buffer; diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 17450ae0ed..550cabbb56 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -12,6 +12,7 @@ #include "Common/EnumMap.h" #include "Common/Logging/Log.h" #include "Common/MathUtil.h" +#include "Common/SmallVector.h" #include "Core/ConfigManager.h" #include "Core/DolphinAnalytics.h" @@ -554,7 +555,7 @@ void VertexManagerBase::Flush() // Calculate ZSlope for zfreeze const auto used_textures = UsedTextures(); std::vector texture_names; - std::vector texture_units; + Common::SmallVector texture_units; if (!m_cull_all) { if (!g_ActiveConfig.bGraphicMods) @@ -599,20 +600,18 @@ void VertexManagerBase::Flush() 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++) { - const std::string& texture_name = texture_names[i]; - const u32 texture_unit = texture_units[i]; - bool skip = false; - GraphicsModActionData::DrawStarted draw_started{texture_unit, &skip, &custom_pixel_shader, + GraphicsModActionData::DrawStarted draw_started{texture_units, &skip, &custom_pixel_shader, &custom_pixel_shader_uniforms}; - for (const auto& action : g_graphics_mod_manager->GetDrawStartedActions(texture_name)) + 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_name); + custom_pixel_texture_names.push_back(texture_names[i]); } custom_pixel_shader = std::nullopt; }