diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp
index 0d8a18fdc3..eb5bc2a891 100644
--- a/Source/Core/Core/AchievementManager.cpp
+++ b/Source/Core/Core/AchievementManager.cpp
@@ -1631,10 +1631,8 @@ AchievementManager::ResponseType AchievementManager::Request(
const std::string response_str(http_response->begin(), http_response->end());
if (process_response(rc_response, response_str.c_str()) != RC_OK)
{
- ERROR_LOG_FMT(
- ACHIEVEMENTS, "Failed to process HTTP response. \nURL: {} \npost_data: {} \nresponse: {}",
- api_request.url, api_request.post_data == nullptr ? "NULL" : api_request.post_data,
- response_str);
+ ERROR_LOG_FMT(ACHIEVEMENTS, "Failed to process HTTP response. \nURL: {} \nresponse: {}",
+ api_request.url, response_str);
return ResponseType::MALFORMED_OBJECT;
}
if (rc_response->response.succeeded)
@@ -1650,9 +1648,7 @@ AchievementManager::ResponseType AchievementManager::Request(
}
else
{
- WARN_LOG_FMT(ACHIEVEMENTS, "RetroAchievements connection failed. \nURL: {} \npost_data: {}",
- api_request.url,
- api_request.post_data == nullptr ? "NULL" : api_request.post_data);
+ WARN_LOG_FMT(ACHIEVEMENTS, "RetroAchievements connection failed. \nURL: {}", api_request.url);
return ResponseType::CONNECTION_FAILED;
}
}
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index cf0788a8dc..59cecdf5dc 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -692,6 +692,7 @@
+
@@ -1323,6 +1324,7 @@
+
diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt
index ebc32ab0c0..36cf30a9d1 100644
--- a/Source/Core/VideoCommon/CMakeLists.txt
+++ b/Source/Core/VideoCommon/CMakeLists.txt
@@ -85,6 +85,8 @@ add_library(videocommon
GraphicsModSystem/Runtime/Actions/ScaleAction.h
GraphicsModSystem/Runtime/Actions/SkipAction.cpp
GraphicsModSystem/Runtime/Actions/SkipAction.h
+ GraphicsModSystem/Runtime/CustomPipeline.cpp
+ GraphicsModSystem/Runtime/CustomPipeline.h
GraphicsModSystem/Runtime/CustomShaderCache.cpp
GraphicsModSystem/Runtime/CustomShaderCache.h
GraphicsModSystem/Runtime/FBInfo.cpp
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
index f457300847..92c048860f 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp
@@ -5,6 +5,7 @@
#include
#include
+#include
#include
@@ -20,164 +21,12 @@
#include "VideoCommon/ShaderGenCommon.h"
#include "VideoCommon/TextureCacheBase.h"
-namespace
+std::unique_ptr
+CustomPipelineAction::Create(std::shared_ptr library)
{
-bool IsQualifier(std::string_view value)
-{
- static std::array qualifiers = {"attribute", "const", "highp", "lowp",
- "mediump", "uniform", "varying"};
- return std::find(qualifiers.begin(), qualifiers.end(), value) != qualifiers.end();
+ return std::make_unique(std::move(library));
}
-bool IsBuiltInMacro(std::string_view value)
-{
- static std::array built_in = {"__LINE__", "__FILE__", "__VERSION__",
- "GL_core_profile", "GL_compatibility_profile"};
- return std::find(built_in.begin(), built_in.end(), value) != built_in.end();
-}
-
-std::vector GlobalConflicts(std::string_view source)
-{
- std::string_view last_identifier = "";
- std::vector global_result;
- u32 scope = 0;
- for (u32 i = 0; i < source.size(); i++)
- {
- // If we're out of global scope, we don't care
- // about any of the details
- if (scope > 0)
- {
- if (source[i] == '{')
- {
- scope++;
- }
- else if (source[i] == '}')
- {
- scope--;
- }
- continue;
- }
-
- const auto parse_identifier = [&]() {
- const u32 start = i;
- for (; i < source.size(); i++)
- {
- if (!Common::IsAlpha(source[i]) && source[i] != '_' && !std::isdigit(source[i]))
- break;
- }
- u32 end = i;
- i--; // unwind
- return source.substr(start, end - start);
- };
-
- if (Common::IsAlpha(source[i]) || source[i] == '_')
- {
- const std::string_view identifier = parse_identifier();
- if (IsQualifier(identifier))
- continue;
- if (IsBuiltInMacro(identifier))
- continue;
- last_identifier = identifier;
- }
- else if (source[i] == '#')
- {
- const auto parse_until_end_of_preprocessor = [&]() {
- bool continue_until_next_newline = false;
- for (; i < source.size(); i++)
- {
- if (source[i] == '\n')
- {
- if (continue_until_next_newline)
- continue_until_next_newline = false;
- else
- break;
- }
- else if (source[i] == '\\')
- {
- continue_until_next_newline = true;
- }
- }
- };
- i++;
- const std::string_view identifier = parse_identifier();
- if (identifier == "define")
- {
- i++;
- // skip whitespace
- while (source[i] == ' ')
- {
- i++;
- }
- global_result.push_back(std::string{parse_identifier()});
- parse_until_end_of_preprocessor();
- }
- else
- {
- parse_until_end_of_preprocessor();
- }
- }
- else if (source[i] == '{')
- {
- scope++;
- }
- else if (source[i] == '(')
- {
- // Unlikely the user will be using layouts but...
- if (last_identifier == "layout")
- continue;
-
- // Since we handle equality, we can assume the identifier
- // before '(' is a function definition
- global_result.push_back(std::string{last_identifier});
- }
- else if (source[i] == '=')
- {
- global_result.push_back(std::string{last_identifier});
- i++;
- for (; i < source.size(); i++)
- {
- if (source[i] == ';')
- break;
- }
- }
- else if (source[i] == '/')
- {
- if ((i + 1) >= source.size())
- continue;
-
- if (source[i + 1] == '/')
- {
- // Go to end of line...
- for (; i < source.size(); i++)
- {
- if (source[i] == '\n')
- break;
- }
- }
- else if (source[i + 1] == '*')
- {
- // Multiline, look for first '*/'
- for (; i < source.size(); i++)
- {
- if (source[i] == '/' && source[i - 1] == '*')
- break;
- }
- }
- }
- }
-
- // Sort the conflicts from largest to smallest string
- // this way we can ensure smaller strings that are a substring
- // of the larger string are able to be replaced appropriately
- std::sort(global_result.begin(), global_result.end(),
- [](const std::string& first, const std::string& second) {
- return first.size() > second.size();
- });
- return global_result;
-}
-
-} // namespace
-
std::unique_ptr
CustomPipelineAction::Create(const picojson::value& json_data,
std::shared_ptr library)
@@ -236,16 +85,19 @@ CustomPipelineAction::Create(const picojson::value& json_data,
return std::make_unique(std::move(library), std::move(pipeline_passes));
}
+CustomPipelineAction::CustomPipelineAction(std::shared_ptr library)
+ : m_library(std::move(library))
+{
+}
+
CustomPipelineAction::CustomPipelineAction(
std::shared_ptr library,
std::vector pass_descriptions)
: m_library(std::move(library)), m_passes_config(std::move(pass_descriptions))
{
- m_passes.resize(m_passes_config.size());
+ m_pipeline_passes.resize(m_passes_config.size());
}
-CustomPipelineAction::~CustomPipelineAction() = default;
-
void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted* draw_started)
{
if (!draw_started) [[unlikely]]
@@ -254,251 +106,20 @@ void CustomPipelineAction::OnDrawStarted(GraphicsModActionData::DrawStarted* dra
if (!draw_started->custom_pixel_shader) [[unlikely]]
return;
- if (!draw_started->material_uniform_buffer) [[unlikely]]
- return;
-
- if (m_passes.empty()) [[unlikely]]
+ if (m_pipeline_passes.empty()) [[unlikely]]
return;
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];
+ auto& pass = m_pipeline_passes[0];
- 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);
- }
-
- const auto material_data = pass.m_pixel_material.m_asset->GetData();
- if (!material_data)
- {
- 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{};
- }
-
- const auto shader_data = pass.m_pixel_shader.m_asset->GetData();
- if (!shader_data)
- {
- return;
- }
-
- 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, has material asset '{}' that uses a "
- "code name of '{}' but that can't be found on shader asset '{}'!",
- pass.m_pixel_material.m_asset->GetAssetId(), property.m_code_name,
- pass.m_pixel_shader.m_asset->GetAssetId());
- return;
- }
-
- if (auto* texture_asset_id =
- std::get_if(&property.m_value))
- {
- if (*texture_asset_id != "")
- {
- 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);
- }
- }
-
- 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& 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);
- }
+ pass.UpdatePixelData(loader, m_library, draw_started->texture_units,
+ pass_config.m_pixel_material_asset);
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();
+ custom_pixel_shader.custom_shader = pass.m_last_generated_shader_code.GetBuffer();
+ custom_pixel_shader.material_uniform_block = pass.m_last_generated_material_code.GetBuffer();
*draw_started->custom_pixel_shader = custom_pixel_shader;
- *draw_started->material_uniform_buffer = m_material_data;
+ *draw_started->material_uniform_buffer = pass.m_material_data;
}
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
index d609f065fb..8413ec187e 100644
--- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.h
@@ -4,20 +4,14 @@
#pragma once
#include
-#include
#include
-#include
#include
#include
-#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/Assets/CustomAssetLibrary.h"
-#include "VideoCommon/Assets/MaterialAsset.h"
-#include "VideoCommon/Assets/ShaderAsset.h"
-#include "VideoCommon/Assets/TextureAsset.h"
+#include "VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModAction.h"
-#include "VideoCommon/ShaderGenCommon.h"
class CustomPipelineAction final : public GraphicsModAction
{
@@ -30,32 +24,15 @@ public:
static std::unique_ptr
Create(const picojson::value& json_data,
std::shared_ptr library);
+ static std::unique_ptr
+ Create(std::shared_ptr library);
+ explicit CustomPipelineAction(std::shared_ptr library);
CustomPipelineAction(std::shared_ptr library,
std::vector pass_descriptions);
- ~CustomPipelineAction();
void OnDrawStarted(GraphicsModActionData::DrawStarted*) override;
private:
std::shared_ptr m_library;
std::vector m_passes_config;
- struct PipelinePass
- {
- VideoCommon::CachedAsset m_pixel_material;
- VideoCommon::CachedAsset m_pixel_shader;
-
- 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;
-
- std::vector m_material_data;
+ std::vector m_pipeline_passes;
};
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.cpp
new file mode 100644
index 0000000000..d6144adea3
--- /dev/null
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.cpp
@@ -0,0 +1,406 @@
+// Copyright 2024 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h"
+
+#include
+#include
+#include
+
+#include "Common/Logging/Log.h"
+#include "Common/VariantUtil.h"
+
+#include "VideoCommon/AbstractGfx.h"
+#include "VideoCommon/Assets/CustomAssetLoader.h"
+
+namespace
+{
+bool IsQualifier(std::string_view value)
+{
+ static std::array qualifiers = {"attribute", "const", "highp", "lowp",
+ "mediump", "uniform", "varying"};
+ return std::find(qualifiers.begin(), qualifiers.end(), value) != qualifiers.end();
+}
+
+bool IsBuiltInMacro(std::string_view value)
+{
+ static std::array built_in = {"__LINE__", "__FILE__", "__VERSION__",
+ "GL_core_profile", "GL_compatibility_profile"};
+ return std::find(built_in.begin(), built_in.end(), value) != built_in.end();
+}
+
+std::vector GlobalConflicts(std::string_view source)
+{
+ std::string_view last_identifier = "";
+ std::vector global_result;
+ u32 scope = 0;
+ for (u32 i = 0; i < source.size(); i++)
+ {
+ // If we're out of global scope, we don't care
+ // about any of the details
+ if (scope > 0)
+ {
+ if (source[i] == '{')
+ {
+ scope++;
+ }
+ else if (source[i] == '}')
+ {
+ scope--;
+ }
+ continue;
+ }
+
+ const auto parse_identifier = [&]() {
+ const u32 start = i;
+ for (; i < source.size(); i++)
+ {
+ if (!Common::IsAlpha(source[i]) && source[i] != '_' && !std::isdigit(source[i]))
+ break;
+ }
+ u32 end = i;
+ i--; // unwind
+ return source.substr(start, end - start);
+ };
+
+ if (Common::IsAlpha(source[i]) || source[i] == '_')
+ {
+ const std::string_view identifier = parse_identifier();
+ if (IsQualifier(identifier))
+ continue;
+ if (IsBuiltInMacro(identifier))
+ continue;
+ last_identifier = identifier;
+ }
+ else if (source[i] == '#')
+ {
+ const auto parse_until_end_of_preprocessor = [&]() {
+ bool continue_until_next_newline = false;
+ for (; i < source.size(); i++)
+ {
+ if (source[i] == '\n')
+ {
+ if (continue_until_next_newline)
+ continue_until_next_newline = false;
+ else
+ break;
+ }
+ else if (source[i] == '\\')
+ {
+ continue_until_next_newline = true;
+ }
+ }
+ };
+ i++;
+ const std::string_view identifier = parse_identifier();
+ if (identifier == "define")
+ {
+ i++;
+ // skip whitespace
+ while (source[i] == ' ')
+ {
+ i++;
+ }
+ global_result.push_back(std::string{parse_identifier()});
+ parse_until_end_of_preprocessor();
+ }
+ else
+ {
+ parse_until_end_of_preprocessor();
+ }
+ }
+ else if (source[i] == '{')
+ {
+ scope++;
+ }
+ else if (source[i] == '(')
+ {
+ // Unlikely the user will be using layouts but...
+ if (last_identifier == "layout")
+ continue;
+
+ // Since we handle equality, we can assume the identifier
+ // before '(' is a function definition
+ global_result.push_back(std::string{last_identifier});
+ }
+ else if (source[i] == '=')
+ {
+ global_result.push_back(std::string{last_identifier});
+ i++;
+ for (; i < source.size(); i++)
+ {
+ if (source[i] == ';')
+ break;
+ }
+ }
+ else if (source[i] == '/')
+ {
+ if ((i + 1) >= source.size())
+ continue;
+
+ if (source[i + 1] == '/')
+ {
+ // Go to end of line...
+ for (; i < source.size(); i++)
+ {
+ if (source[i] == '\n')
+ break;
+ }
+ }
+ else if (source[i + 1] == '*')
+ {
+ // Multiline, look for first '*/'
+ for (; i < source.size(); i++)
+ {
+ if (source[i] == '/' && source[i - 1] == '*')
+ break;
+ }
+ }
+ }
+ }
+
+ // Sort the conflicts from largest to smallest string
+ // this way we can ensure smaller strings that are a substring
+ // of the larger string are able to be replaced appropriately
+ std::sort(global_result.begin(), global_result.end(),
+ [](const std::string& first, const std::string& second) {
+ return first.size() > second.size();
+ });
+ return global_result;
+}
+
+} // namespace
+
+void CustomPipeline::UpdatePixelData(
+ VideoCommon::CustomAssetLoader& loader,
+ std::shared_ptr library, std::span texture_units,
+ const VideoCommon::CustomAssetLibrary::AssetID& material_to_load)
+{
+ if (!m_pixel_material.m_asset || material_to_load != m_pixel_material.m_asset->GetAssetId())
+ {
+ m_pixel_material.m_asset = loader.LoadMaterial(material_to_load, library);
+ }
+
+ const auto material_data = m_pixel_material.m_asset->GetData();
+ if (!material_data)
+ {
+ return;
+ }
+
+ std::size_t max_material_data_size = 0;
+ if (m_pixel_material.m_asset->GetLastLoadedTime() > m_pixel_material.m_cached_write_time)
+ {
+ m_last_generated_material_code = ShaderCode{};
+ m_pixel_material.m_cached_write_time = 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);
+ m_game_textures.resize(texture_count);
+ }
+
+ if (!m_pixel_shader.m_asset ||
+ m_pixel_shader.m_asset->GetLastLoadedTime() > m_pixel_shader.m_cached_write_time ||
+ material_data->shader_asset != m_pixel_shader.m_asset->GetAssetId())
+ {
+ m_pixel_shader.m_asset = loader.LoadPixelShader(material_data->shader_asset, library);
+ m_pixel_shader.m_cached_write_time = m_pixel_shader.m_asset->GetLastLoadedTime();
+
+ m_last_generated_shader_code = ShaderCode{};
+ }
+
+ const auto shader_data = m_pixel_shader.m_asset->GetData();
+ if (!shader_data)
+ {
+ return;
+ }
+
+ 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, has material asset '{}' that uses a "
+ "code name of '{}' but that can't be found on shader asset '{}'!",
+ m_pixel_material.m_asset->GetAssetId(), property.m_code_name,
+ m_pixel_shader.m_asset->GetAssetId());
+ return;
+ }
+
+ if (auto* texture_asset_id =
+ std::get_if(&property.m_value))
+ {
+ if (*texture_asset_id != "")
+ {
+ auto asset = loader.LoadGameTexture(*texture_asset_id, library);
+ if (!asset)
+ {
+ return;
+ }
+
+ auto& texture_asset = 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 = 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, RenderState::GetLinearSamplerState());
+ }
+ else
+ {
+ AbstractTextureType texture_type = AbstractTextureType::Texture_2DArray;
+ if (std::holds_alternative(
+ shader_it->second.m_default))
+ {
+ texture_type = AbstractTextureType::Texture_CubeMap;
+ }
+ else if (std::holds_alternative(
+ shader_it->second.m_default))
+ {
+ texture_type = 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_type);
+ 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);
+ }
+ }
+
+ 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& game_texture : 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 < texture_units.size(); i++)
+ {
+ const auto& texture_unit = 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);
+ }
+}
diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h
new file mode 100644
index 0000000000..83bc9f84e1
--- /dev/null
+++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h
@@ -0,0 +1,48 @@
+// Copyright 2024 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "VideoCommon/AbstractTexture.h"
+#include "VideoCommon/Assets/CustomAssetLibrary.h"
+#include "VideoCommon/Assets/MaterialAsset.h"
+#include "VideoCommon/Assets/ShaderAsset.h"
+#include "VideoCommon/Assets/TextureAsset.h"
+#include "VideoCommon/ShaderGenCommon.h"
+
+namespace VideoCommon
+{
+class CustomAssetLoader;
+}
+
+struct CustomPipeline
+{
+ void UpdatePixelData(VideoCommon::CustomAssetLoader& loader,
+ std::shared_ptr library,
+ std::span texture_units,
+ const VideoCommon::CustomAssetLibrary::AssetID& material_to_load);
+
+ VideoCommon::CachedAsset m_pixel_material;
+ VideoCommon::CachedAsset m_pixel_shader;
+
+ 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;
+
+ ShaderCode m_last_generated_shader_code;
+ ShaderCode m_last_generated_material_code;
+
+ std::vector m_material_data;
+};
diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp
index fff2374531..0d6efe9f6a 100644
--- a/Source/Core/VideoCommon/PixelShaderGen.cpp
+++ b/Source/Core/VideoCommon/PixelShaderGen.cpp
@@ -839,7 +839,8 @@ void WriteCustomShaderStructImpl(ShaderCode* out, u32 num_stages, bool per_pixel
texcoord);
}
- GenerateCustomLightingImplementation(out, uid_data->lighting, "colors_");
+ if (per_pixel_lighting)
+ GenerateCustomLightingImplementation(out, uid_data->lighting, "colors_");
for (u32 i = 0; i < 16; i++)
{
@@ -1326,8 +1327,20 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
if (!shader_details.custom_shader.empty())
{
out.Write("\t{{\n");
- out.Write("\t\tcustom_data.final_color = ocol0;\n");
- out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ if (uid_data->uint_output)
+ {
+ out.Write("\t\tcustom_data.final_color = float4(ocol0.x / 255.0, ocol0.y / 255.0, ocol0.z "
+ "/ 255.0, ocol0.w / 255.0);\n");
+ out.Write("\t\tfloat3 custom_output = {}_{}(custom_data).xyz;\n",
+ CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ out.Write("\t\tocol0.xyz = uint3(custom_output.x * 255, custom_output.y * 255, "
+ "custom_output.z * 255);\n");
+ }
+ else
+ {
+ out.Write("\t\tcustom_data.final_color = ocol0;\n");
+ out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ }
out.Write("\t}}\n\n");
}
}
diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp
index 1458e3c59f..7b66269ec7 100644
--- a/Source/Core/VideoCommon/PostProcessing.cpp
+++ b/Source/Core/VideoCommon/PostProcessing.cpp
@@ -524,9 +524,9 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst,
needs_resampling ? present_rect.GetHeight() : static_cast(src_rect.GetHeight());
if (!m_intermediary_frame_buffer || !m_intermediary_color_texture ||
- m_intermediary_color_texture.get()->GetWidth() != target_width ||
- m_intermediary_color_texture.get()->GetHeight() != target_height ||
- m_intermediary_color_texture.get()->GetLayers() != target_layers)
+ m_intermediary_color_texture->GetWidth() != target_width ||
+ m_intermediary_color_texture->GetHeight() != target_height ||
+ m_intermediary_color_texture->GetLayers() != target_layers)
{
const TextureConfig intermediary_color_texture_config(
target_width, target_height, 1, target_layers, src_tex->GetSamples(),
@@ -580,8 +580,8 @@ void PostProcessing::BlitFromTexture(const MathUtil::Rectangle& dst,
default_uniform_staging_buffer = false;
}
- m_intermediary_frame_buffer.release();
- m_intermediary_color_texture.release();
+ m_intermediary_frame_buffer.reset();
+ m_intermediary_color_texture.reset();
}
// TODO: ideally we'd do the user selected post process pass in the intermediary buffer in linear
diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp
index a4874fd388..2409dc41eb 100644
--- a/Source/Core/VideoCommon/UberShaderPixel.cpp
+++ b/Source/Core/VideoCommon/UberShaderPixel.cpp
@@ -127,106 +127,109 @@ void WriteCustomShaderStructImpl(ShaderCode* out, u32 num_texgen, bool per_pixel
out->Write("\t}}\n");
}
- out->Write("\tuint light_count = 0;\n");
- out->Write("\tfor (uint chan = 0u; chan < {}u; chan++)\n", NUM_XF_COLOR_CHANNELS);
- out->Write("\t{{\n");
- out->Write("\t\tuint colorreg = xfmem_color(chan);\n");
- out->Write("\t\tuint alphareg = xfmem_alpha(chan);\n");
- for (const auto& color_type : std::array{"colorreg", "alphareg"})
+ if (per_pixel_lighting)
{
- if (color_type == "colorreg")
+ out->Write("\tuint light_count = 0;\n");
+ out->Write("\tfor (uint chan = 0u; chan < {}u; chan++)\n", NUM_XF_COLOR_CHANNELS);
+ out->Write("\t{{\n");
+ out->Write("\t\tuint colorreg = xfmem_color(chan);\n");
+ out->Write("\t\tuint alphareg = xfmem_alpha(chan);\n");
+ for (const auto& color_type : std::array{"colorreg", "alphareg"})
{
- out->Write("\t\tcustom_data.base_material[0] = " I_MATERIALS "[2u] / 255.0; \n");
- out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
- out->Write("\t\t\tcustom_data.base_material[0] = colors_0; \n");
- }
- else
- {
- out->Write("custom_data.base_material[1].w = " I_MATERIALS "[3u].w / 255.0; \n");
- out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
- out->Write("\t\t\tcustom_data.base_material[1].w = colors_1.w; \n");
- }
- out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
- out->Write("\t\t{{\n");
- out->Write("\t\t\tuint light_mask = {} | ({} << 4u);\n",
- BitfieldExtract<&LitChannel::lightMask0_3>(color_type),
- BitfieldExtract<&LitChannel::lightMask4_7>(color_type));
- out->Write("\t\t\tuint attnfunc = {};\n", BitfieldExtract<&LitChannel::attnfunc>(color_type));
- out->Write("\t\t\tfor (uint light_index = 0u; light_index < 8u; light_index++)\n");
- out->Write("\t\t\t{{\n");
- out->Write("\t\t\t\tif ((light_mask & (1u << light_index)) != 0u)\n");
- out->Write("\t\t\t\t{{\n");
- // Shader compilation is weird, shader arrays can't use indexing by variable
- // to set values unless the variable is an index in a for loop.
- // So instead we have to do this if check nonsense
- for (u32 light_count_index = 0; light_count_index < 8; light_count_index++)
- {
- out->Write("\t\t\t\t\tif (light_index == {})\n", light_count_index);
- out->Write("\t\t\t\t\t{{\n");
if (color_type == "colorreg")
{
- for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
- {
- out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
- out->Write("\t\t\t\t\t\t{{\n");
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].direction = " I_LIGHTS
- "[light_index].dir.xyz;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].position = " I_LIGHTS
- "[light_index].pos.xyz;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].cosatt = " I_LIGHTS
- "[light_index].cosatt;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].distatt = " I_LIGHTS
- "[light_index].distatt;\n",
- channel_index, light_count_index);
- out->Write(
- "\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].attenuation_type = attnfunc;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].color = " I_LIGHTS
- "[light_index].color.rgb / float3(255.0, 255.0, 255.0);\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_color_count += 1;\n", channel_index);
- out->Write("\t\t\t\t\t\t}}\n");
- }
+ out->Write("\t\tcustom_data.base_material[0] = " I_MATERIALS "[2u] / 255.0; \n");
+ out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
+ out->Write("\t\t\tcustom_data.base_material[0] = colors_0; \n");
}
else
{
- for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
- {
- out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
- out->Write("\t\t\t\t\t\t{{\n");
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].direction = " I_LIGHTS
- "[light_index].dir.xyz;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].position = " I_LIGHTS
- "[light_index].pos.xyz;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].cosatt = " I_LIGHTS
- "[light_index].cosatt;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].distatt = " I_LIGHTS
- "[light_index].distatt;\n",
- channel_index, light_count_index);
- out->Write(
- "\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].attenuation_type = attnfunc;\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].color = float3(" I_LIGHTS
- "[light_index].color.a) / float3(255.0, 255.0, 255.0);\n",
- channel_index, light_count_index);
- out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_alpha_count += 1;\n", channel_index);
- out->Write("\t\t\t\t\t\t}}\n");
- }
+ out->Write("custom_data.base_material[1].w = " I_MATERIALS "[3u].w / 255.0; \n");
+ out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
+ out->Write("\t\t\tcustom_data.base_material[1].w = colors_1.w; \n");
}
+ out->Write("\t\tif ({} != 0u)\n", BitfieldExtract<&LitChannel::enablelighting>(color_type));
+ out->Write("\t\t{{\n");
+ out->Write("\t\t\tuint light_mask = {} | ({} << 4u);\n",
+ BitfieldExtract<&LitChannel::lightMask0_3>(color_type),
+ BitfieldExtract<&LitChannel::lightMask4_7>(color_type));
+ out->Write("\t\t\tuint attnfunc = {};\n", BitfieldExtract<&LitChannel::attnfunc>(color_type));
+ out->Write("\t\t\tfor (uint light_index = 0u; light_index < 8u; light_index++)\n");
+ out->Write("\t\t\t{{\n");
+ out->Write("\t\t\t\tif ((light_mask & (1u << light_index)) != 0u)\n");
+ out->Write("\t\t\t\t{{\n");
+ // Shader compilation is weird, shader arrays can't use indexing by variable
+ // to set values unless the variable is an index in a for loop.
+ // So instead we have to do this if check nonsense
+ for (u32 light_count_index = 0; light_count_index < 8; light_count_index++)
+ {
+ out->Write("\t\t\t\t\tif (light_index == {})\n", light_count_index);
+ out->Write("\t\t\t\t\t{{\n");
+ if (color_type == "colorreg")
+ {
+ for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
+ {
+ out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
+ out->Write("\t\t\t\t\t\t{{\n");
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].direction = " I_LIGHTS
+ "[light_index].dir.xyz;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].position = " I_LIGHTS
+ "[light_index].pos.xyz;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].cosatt = " I_LIGHTS
+ "[light_index].cosatt;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].distatt = " I_LIGHTS
+ "[light_index].distatt;\n",
+ channel_index, light_count_index);
+ out->Write(
+ "\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].attenuation_type = attnfunc;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_color[{}].color = " I_LIGHTS
+ "[light_index].color.rgb / float3(255.0, 255.0, 255.0);\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_color_count += 1;\n", channel_index);
+ out->Write("\t\t\t\t\t\t}}\n");
+ }
+ }
+ else
+ {
+ for (u32 channel_index = 0; channel_index < NUM_XF_COLOR_CHANNELS; channel_index++)
+ {
+ out->Write("\t\t\t\t\t\tif (chan == {})\n", channel_index);
+ out->Write("\t\t\t\t\t\t{{\n");
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].direction = " I_LIGHTS
+ "[light_index].dir.xyz;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].position = " I_LIGHTS
+ "[light_index].pos.xyz;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].cosatt = " I_LIGHTS
+ "[light_index].cosatt;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].distatt = " I_LIGHTS
+ "[light_index].distatt;\n",
+ channel_index, light_count_index);
+ out->Write(
+ "\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].attenuation_type = attnfunc;\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.lights_chan{}_alpha[{}].color = float3(" I_LIGHTS
+ "[light_index].color.a) / float3(255.0, 255.0, 255.0);\n",
+ channel_index, light_count_index);
+ out->Write("\t\t\t\t\t\t\tcustom_data.light_chan{}_alpha_count += 1;\n", channel_index);
+ out->Write("\t\t\t\t\t\t}}\n");
+ }
+ }
- out->Write("\t\t\t\t\t}}\n");
+ out->Write("\t\t\t\t\t}}\n");
+ }
+ out->Write("\t\t\t\t}}\n");
+ out->Write("\t\t\t}}\n");
+ out->Write("\t\t}}\n");
}
- out->Write("\t\t\t\t}}\n");
- out->Write("\t\t\t}}\n");
- out->Write("\t\t}}\n");
+ out->Write("\t}}\n");
}
- out->Write("\t}}\n");
for (u32 i = 0; i < 16; i++)
{
@@ -1593,8 +1596,20 @@ ShaderCode GenPixelShader(APIType api_type, const ShaderHostConfig& host_config,
if (!shader_details.custom_shader.empty())
{
out.Write("\t{{\n");
- out.Write("\t\tcustom_data.final_color = ocol0;\n");
- out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ if (uid_data->uint_output)
+ {
+ out.Write("\t\tcustom_data.final_color = float4(ocol0.x / 255.0, ocol0.y / 255.0, ocol0.z "
+ "/ 255.0, ocol0.w / 255.0);\n");
+ out.Write("\t\tfloat3 custom_output = {}_{}(custom_data).xyz;\n",
+ CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ out.Write("\t\tocol0.xyz = uint3(custom_output.x * 255, custom_output.y * 255, "
+ "custom_output.z * 255);\n");
+ }
+ else
+ {
+ out.Write("\t\tcustom_data.final_color = ocol0;\n");
+ out.Write("\t\tocol0.xyz = {}_{}(custom_data).xyz;\n", CUSTOM_PIXELSHADER_COLOR_FUNC, i);
+ }
out.Write("\t}}\n\n");
}
}
diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp
index e4f6ef9921..92ff2a861f 100644
--- a/Source/Core/VideoCommon/VertexManagerBase.cpp
+++ b/Source/Core/VideoCommon/VertexManagerBase.cpp
@@ -615,8 +615,6 @@ void VertexManagerBase::Flush()
}
custom_pixel_shader = std::nullopt;
}
- if (skip == true)
- return;
}
// Now the vertices can be flushed to the GPU. Everything following the CommitBuffer() call
@@ -624,116 +622,47 @@ void VertexManagerBase::Flush()
const u32 num_indices = m_index_generator.GetIndexLen();
if (num_indices == 0)
return;
- u32 base_vertex, base_index;
- CommitBuffer(m_index_generator.GetNumVerts(),
- VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(), num_indices,
- &base_vertex, &base_index);
-
- 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
- // (The shader adds this after shifting right on D3D, so no need to do this)
- base_vertex <<= 2;
- }
// Texture loading can cause palettes to be applied (-> uniforms -> draws).
// Palette application does not use vertices, only a full-screen quad, so this is okay.
// Same with GPU texture decoding, which uses compute shaders.
g_texture_cache->BindTextures(used_textures);
- // Now we can upload uniforms, as nothing else will override them.
- 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();
+ if (PerfQueryBase::ShouldEmulate())
+ g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
- // Update the pipeline, or compile one if needed.
- UpdatePipelineConfig();
- UpdatePipelineObject();
- if (m_current_pipeline_object)
+ if (!skip)
{
- const AbstractPipeline* current_pipeline = m_current_pipeline_object;
- if (!custom_pixel_shader_contents.shaders.empty())
+ UpdatePipelineConfig();
+ UpdatePipelineObject();
+ if (m_current_pipeline_object)
{
- CustomShaderInstance custom_shaders;
- custom_shaders.pixel_contents = std::move(custom_pixel_shader_contents);
-
- switch (g_ActiveConfig.iShaderCompilationMode)
+ const AbstractPipeline* pipeline_object = m_current_pipeline_object;
+ if (!custom_pixel_shader_contents.shaders.empty())
{
- case ShaderCompilationMode::Synchronous:
- case ShaderCompilationMode::AsynchronousSkipRendering:
- {
- if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
- m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
+ if (const auto custom_pipeline =
+ GetCustomPipeline(custom_pixel_shader_contents, m_current_pipeline_config,
+ m_current_uber_pipeline_config, m_current_pipeline_object))
{
- current_pipeline = *pipeline;
+ pipeline_object = custom_pipeline;
}
}
- break;
- case ShaderCompilationMode::SynchronousUberShaders:
- {
- // D3D has issues compiling large custom ubershaders
- // use specialized shaders instead
- if (g_ActiveConfig.backend_info.api_type == APIType::D3D)
- {
- if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
- m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
- {
- current_pipeline = *pipeline;
- }
- }
- else
- {
- if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
- m_current_uber_pipeline_config, custom_shaders,
- m_current_pipeline_object->m_config))
- {
- current_pipeline = *pipeline;
- }
- }
- }
- break;
- case ShaderCompilationMode::AsynchronousUberShaders:
- {
- if (auto pipeline = m_custom_shader_cache->GetPipelineAsync(
- m_current_pipeline_config, custom_shaders, m_current_pipeline_object->m_config))
- {
- current_pipeline = *pipeline;
- }
- else if (auto uber_pipeline = m_custom_shader_cache->GetPipelineAsync(
- m_current_uber_pipeline_config, custom_shaders,
- m_current_pipeline_object->m_config))
- {
- current_pipeline = *uber_pipeline;
- }
- }
- break;
- };
+ RenderDrawCall(pixel_shader_manager, geometry_shader_manager, custom_pixel_shader_contents,
+ custom_pixel_shader_uniforms, m_current_primitive_type, pipeline_object);
}
- g_gfx->SetPipeline(current_pipeline);
- if (PerfQueryBase::ShouldEmulate())
- g_perf_query->EnableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
-
- DrawCurrentBatch(base_index, num_indices, base_vertex);
- INCSTAT(g_stats.this_frame.num_draw_calls);
-
- if (PerfQueryBase::ShouldEmulate())
- g_perf_query->DisableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
-
- OnDraw();
-
- // The EFB cache is now potentially stale.
- g_framebuffer_manager->FlagPeekCacheAsOutOfDate();
}
+
+ // 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
+ OnDraw();
+
+ if (PerfQueryBase::ShouldEmulate())
+ g_perf_query->DisableQuery(bpmem.zcontrol.early_ztest ? PQG_ZCOMP_ZCOMPLOC : PQG_ZCOMP);
+
+ // The EFB cache is now potentially stale.
+ g_framebuffer_manager->FlagPeekCacheAsOutOfDate();
}
if (xfmem.numTexGen.numTexGens != bpmem.genMode.numtexgens)
@@ -1122,3 +1051,107 @@ void VertexManagerBase::NotifyCustomShaderCacheOfHostChange(const ShaderHostConf
m_custom_shader_cache->SetHostConfig(host_config);
m_custom_shader_cache->Reload();
}
+
+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);
+ 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);
+
+ u32 base_vertex, base_index;
+ CommitBuffer(m_index_generator.GetNumVerts(),
+ VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(),
+ m_index_generator.GetIndexLen(), &base_vertex, &base_index);
+
+ if (g_ActiveConfig.backend_info.api_type != APIType::D3D &&
+ g_ActiveConfig.UseVSForLinePointExpand() &&
+ (primitive_type == PrimitiveType::Points || 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);
+}
+
+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
+{
+ if (current_pipeline)
+ {
+ if (!custom_pixel_shader_contents.shaders.empty())
+ {
+ 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_ActiveConfig.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;
+ };
+ }
+ }
+
+ return nullptr;
+}
diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h
index ebb857ff21..50032419bd 100644
--- a/Source/Core/VideoCommon/VertexManagerBase.h
+++ b/Source/Core/VideoCommon/VertexManagerBase.h
@@ -15,9 +15,12 @@
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/VideoEvents.h"
+struct CustomPixelShaderContents;
class CustomShaderCache;
class DataReader;
+class GeometryShaderManager;
class NativeVertexFormat;
+class PixelShaderManager;
class PointerWrap;
struct PortableVertexDeclaration;
@@ -218,9 +221,20 @@ 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 = {};