From a3f9f2c7aaaf9225736162282a8f3d0c47f1fc4f Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 23 Jan 2024 13:34:13 -0500 Subject: [PATCH 1/9] PostProcessing: Remove unnecessary get() calls in BlitFromTexture() We can just use operator-> instead. --- Source/Core/VideoCommon/PostProcessing.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 1458e3c59f..3f7682ef96 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(), From 5aeadb1ef8cd27fc5b5071aac20aa7480c0eb7c0 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 23 Jan 2024 13:29:26 -0500 Subject: [PATCH 2/9] PostProcessing: Don't potentially leak memory in BlitFromTexture() All release() does is relinquish the pointer, rather than free the memory associated with it. --- Source/Core/VideoCommon/PostProcessing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 3f7682ef96..7b66269ec7 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -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 From a37fd83218616b3e07c98d07f338515e924e3781 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 22 Jan 2024 21:30:37 -0600 Subject: [PATCH 3/9] VideoCommon: fix uber shader pixel compilation error that happens when uint output is defined --- Source/Core/VideoCommon/UberShaderPixel.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp index a4874fd388..1117352dfd 100644 --- a/Source/Core/VideoCommon/UberShaderPixel.cpp +++ b/Source/Core/VideoCommon/UberShaderPixel.cpp @@ -1593,8 +1593,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"); } } From 7118fc5b7bd9eb3d83ece9a2dae31708da46b2c8 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 22 Jan 2024 21:31:03 -0600 Subject: [PATCH 4/9] VideoCommon: fix pixel shader compilation error that happens when uint output is defined --- Source/Core/VideoCommon/PixelShaderGen.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index fff2374531..61a351742b 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -1326,8 +1326,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"); } } From a8d45b8e55969ea7e979f03372db5cf904ca0344 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 22 Jan 2024 21:46:21 -0600 Subject: [PATCH 5/9] VideoCommon: fix compilation error in uber pixel shaders when pixel shader lighting isn't set for custom shaders --- Source/Core/VideoCommon/UberShaderPixel.cpp | 181 ++++++++++---------- 1 file changed, 92 insertions(+), 89 deletions(-) diff --git a/Source/Core/VideoCommon/UberShaderPixel.cpp b/Source/Core/VideoCommon/UberShaderPixel.cpp index 1117352dfd..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++) { From cf081e839da523fc2e22e7ac6aef404b173079e1 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Mon, 22 Jan 2024 21:46:55 -0600 Subject: [PATCH 6/9] VideoCommon: fix compilation error in pixel shaders when per-pixel lighting isn't set for custom shaders --- Source/Core/VideoCommon/PixelShaderGen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 61a351742b..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++) { From 3e3967ff942dffc89bae36d3c48007e8bfa767d2 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Sat, 27 Jan 2024 00:12:49 -0600 Subject: [PATCH 7/9] VideoCommon: refactor the custom pipeline logic in the graphics mod action into a separate class, so it is reusable --- Source/Core/DolphinLib.props | 2 + Source/Core/VideoCommon/CMakeLists.txt | 2 + .../Runtime/Actions/CustomPipelineAction.cpp | 413 +----------------- .../Runtime/Actions/CustomPipelineAction.h | 33 +- .../Runtime/CustomPipeline.cpp | 406 +++++++++++++++++ .../Runtime/CustomPipeline.h | 48 ++ 6 files changed, 480 insertions(+), 424 deletions(-) create mode 100644 Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.cpp create mode 100644 Source/Core/VideoCommon/GraphicsModSystem/Runtime/CustomPipeline.h 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; +}; From c34b3ae3900280a23630c07ee6e7e3018d48ee04 Mon Sep 17 00:00:00 2001 From: iwubcode Date: Sat, 27 Jan 2024 14:21:42 -0600 Subject: [PATCH 8/9] VideoCommon: refactor drawing into its own function --- Source/Core/VideoCommon/VertexManagerBase.cpp | 227 ++++++++++-------- Source/Core/VideoCommon/VertexManagerBase.h | 14 ++ 2 files changed, 144 insertions(+), 97 deletions(-) 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 = {}; From f25bdda72877b9e09cc9c1985c51721d4194dceb Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Sat, 27 Jan 2024 18:50:27 -0500 Subject: [PATCH 9/9] Removed post data from Achievement request logs post_data included passwords and API keys in plaintext so continuing to include it in logs is a security liability. --- Source/Core/Core/AchievementManager.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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; } }