diff options
Diffstat (limited to 'Source/Core/VideoCommon/PostProcessing.cpp')
| -rw-r--r-- | Source/Core/VideoCommon/PostProcessing.cpp | 499 |
1 files changed, 422 insertions, 77 deletions
diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index a83469e451..b1c9c42056 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -5,74 +5,44 @@ #include <sstream> #include <string> +#include "Common/Assert.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/FileSearch.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" #include "Common/StringUtil.h" +#include "VideoCommon/AbstractFramebuffer.h" +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/FramebufferManager.h" #include "VideoCommon/PostProcessing.h" +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/ShaderCache.h" +#include "VideoCommon/VertexManagerBase.h" #include "VideoCommon/VideoConfig.h" -static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n"; - -PostProcessingShaderImplementation::PostProcessingShaderImplementation() -{ - m_timer.Start(); -} - -PostProcessingShaderImplementation::~PostProcessingShaderImplementation() -{ - m_timer.Stop(); -} - -static std::vector<std::string> GetShaders(const std::string& sub_dir = "") -{ - std::vector<std::string> paths = - Common::DoFileSearch({File::GetUserPath(D_SHADERS_IDX) + sub_dir, - File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}, - {".glsl"}); - std::vector<std::string> result; - for (std::string path : paths) - { - std::string name; - SplitPath(path, nullptr, &name, nullptr); - result.push_back(name); - } - return result; -} - -std::vector<std::string> PostProcessingShaderImplementation::GetShaderList(APIType api_type) +namespace VideoCommon { - // Currently there is no differentiation between API types and shader languages. - // This could change in the future, hence the api_type parameter, but ideally, - // shaders should be compatible across backends. - if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) - return GetShaders(); - - return {}; -} - -std::vector<std::string> PostProcessingShaderImplementation::GetAnaglyphShaderList(APIType api_type) -{ - if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) - return GetShaders(ANAGLYPH_DIR DIR_SEP); - - return {}; -} +static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n"; -PostProcessingShaderConfiguration::PostProcessingShaderConfiguration() = default; +PostProcessingConfiguration::PostProcessingConfiguration() = default; -PostProcessingShaderConfiguration::~PostProcessingShaderConfiguration() = default; +PostProcessingConfiguration::~PostProcessingConfiguration() = default; -std::string PostProcessingShaderConfiguration::LoadShader(std::string shader) +void PostProcessingConfiguration::LoadShader(const std::string& shader) { // Load the shader from the configuration if there isn't one sent to us. - if (shader.empty()) - shader = g_ActiveConfig.sPostProcessingShader; m_current_shader = shader; + if (shader.empty()) + { + LoadDefaultShader(); + return; + } const std::string sub_dir = (g_Config.stereo_mode == StereoMode::Anaglyph) ? ANAGLYPH_DIR DIR_SEP : ""; @@ -81,32 +51,32 @@ std::string PostProcessingShaderConfiguration::LoadShader(std::string shader) std::string code; std::string path = File::GetUserPath(D_SHADERS_IDX) + sub_dir + shader + ".glsl"; - if (shader.empty()) + if (!File::Exists(path)) { - code = s_default_shader; + // Fallback to shared user dir + path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir + shader + ".glsl"; } - else - { - if (!File::Exists(path)) - { - // Fallback to shared user dir - path = File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir + shader + ".glsl"; - } - if (!File::ReadFileToString(path, code)) - { - ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str()); - code = s_default_shader; - } + if (!File::ReadFileToString(path, code)) + { + ERROR_LOG(VIDEO, "Post-processing shader not found: %s", path.c_str()); + LoadDefaultShader(); + return; } LoadOptions(code); LoadOptionsConfiguration(); + m_current_shader_code = code; +} - return code; +void PostProcessingConfiguration::LoadDefaultShader() +{ + m_options.clear(); + m_any_options_dirty = false; + m_current_shader_code = s_default_shader; } -void PostProcessingShaderConfiguration::LoadOptions(const std::string& code) +void PostProcessingConfiguration::LoadOptions(const std::string& code) { const std::string config_start_delimiter = "[configuration]"; const std::string config_end_delimiter = "[/configuration]"; @@ -254,7 +224,7 @@ void PostProcessingShaderConfiguration::LoadOptions(const std::string& code) } } -void PostProcessingShaderConfiguration::LoadOptionsConfiguration() +void PostProcessingConfiguration::LoadOptionsConfiguration() { IniFile ini; ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); @@ -288,7 +258,7 @@ void PostProcessingShaderConfiguration::LoadOptionsConfiguration() } } -void PostProcessingShaderConfiguration::SaveOptionsConfiguration() +void PostProcessingConfiguration::SaveOptionsConfiguration() { IniFile ini; ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); @@ -331,13 +301,7 @@ void PostProcessingShaderConfiguration::SaveOptionsConfiguration() ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX)); } -void PostProcessingShaderConfiguration::ReloadShader() -{ - m_current_shader = ""; -} - -void PostProcessingShaderConfiguration::SetOptionf(const std::string& option, int index, - float value) +void PostProcessingConfiguration::SetOptionf(const std::string& option, int index, float value) { auto it = m_options.find(option); @@ -346,7 +310,7 @@ void PostProcessingShaderConfiguration::SetOptionf(const std::string& option, in m_any_options_dirty = true; } -void PostProcessingShaderConfiguration::SetOptioni(const std::string& option, int index, s32 value) +void PostProcessingConfiguration::SetOptioni(const std::string& option, int index, s32 value) { auto it = m_options.find(option); @@ -355,7 +319,7 @@ void PostProcessingShaderConfiguration::SetOptioni(const std::string& option, in m_any_options_dirty = true; } -void PostProcessingShaderConfiguration::SetOptionb(const std::string& option, bool value) +void PostProcessingConfiguration::SetOptionb(const std::string& option, bool value) { auto it = m_options.find(option); @@ -363,3 +327,384 @@ void PostProcessingShaderConfiguration::SetOptionb(const std::string& option, bo it->second.m_dirty = true; m_any_options_dirty = true; } + +PostProcessing::PostProcessing() +{ + m_timer.Start(); +} + +PostProcessing::~PostProcessing() +{ + m_timer.Stop(); +} + +static std::vector<std::string> GetShaders(const std::string& sub_dir = "") +{ + std::vector<std::string> paths = + Common::DoFileSearch({File::GetUserPath(D_SHADERS_IDX) + sub_dir, + File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}, + {".glsl"}); + std::vector<std::string> result; + for (std::string path : paths) + { + std::string name; + SplitPath(path, nullptr, &name, nullptr); + result.push_back(name); + } + return result; +} + +std::vector<std::string> PostProcessing::GetShaderList() +{ + return GetShaders(); +} + +std::vector<std::string> PostProcessing::GetAnaglyphShaderList() +{ + return GetShaders(ANAGLYPH_DIR DIR_SEP); +} + +bool PostProcessing::Initialize(AbstractTextureFormat format) +{ + m_framebuffer_format = format; + if (!CompileVertexShader() || !CompilePixelShader() || !CompilePipeline()) + return false; + + return true; +} + +void PostProcessing::RecompileShader() +{ + m_pipeline.reset(); + m_pixel_shader.reset(); + if (!CompilePixelShader()) + return; + + CompilePipeline(); +} + +void PostProcessing::RecompilePipeline() +{ + m_pipeline.reset(); + CompilePipeline(); +} + +void PostProcessing::BlitFromTexture(const MathUtil::Rectangle<int>& dst, + const MathUtil::Rectangle<int>& src, + const AbstractTexture* src_tex, int src_layer) +{ + if (g_renderer->GetCurrentFramebuffer()->GetColorFormat() != m_framebuffer_format) + { + m_framebuffer_format = g_renderer->GetCurrentFramebuffer()->GetColorFormat(); + RecompilePipeline(); + } + + if (!m_pipeline) + return; + + FillUniformBuffer(src, src_tex, src_layer); + g_vertex_manager->UploadUtilityUniforms(m_uniform_staging_buffer.data(), + static_cast<u32>(m_uniform_staging_buffer.size())); + + g_renderer->SetViewportAndScissor( + g_renderer->ConvertFramebufferRectangle(dst, g_renderer->GetCurrentFramebuffer())); + g_renderer->SetPipeline(m_pipeline.get()); + g_renderer->SetTexture(0, src_tex); + g_renderer->SetSamplerState(0, RenderState::GetLinearSamplerState()); + g_renderer->Draw(0, 3); +} + +std::string PostProcessing::GetUniformBufferHeader() const +{ + std::stringstream ss; + u32 unused_counter = 1; + if (g_ActiveConfig.backend_info.api_type == APIType::D3D) + ss << "cbuffer PSBlock : register(b0) {\n"; + else + ss << "UBO_BINDING(std140, 1) uniform PSBlock {\n"; + + // Builtin uniforms + ss << " float4 resolution;\n"; + ss << " float4 src_rect;\n"; + ss << " uint time;\n"; + ss << " int layer;\n"; + for (u32 i = 0; i < 2; i++) + ss << " uint ubo_align_" << unused_counter++ << "_;\n"; + ss << "\n"; + + // Custom options/uniforms + for (const auto& it : m_config.GetOptions()) + { + if (it.second.m_type == + PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_BOOL) + { + ss << StringFromFormat(" int %s;\n", it.first.c_str()); + for (u32 i = 0; i < 3; i++) + ss << " int ubo_align_" << unused_counter++ << "_;\n"; + } + else if (it.second.m_type == + PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + { + u32 count = static_cast<u32>(it.second.m_integer_values.size()); + if (count == 1) + ss << StringFromFormat(" int %s;\n", it.first.c_str()); + else + ss << StringFromFormat(" int%u %s;\n", count, it.first.c_str()); + + for (u32 i = count; i < 4; i++) + ss << " int ubo_align_" << unused_counter++ << "_;\n"; + } + else if (it.second.m_type == + PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT) + { + u32 count = static_cast<u32>(it.second.m_float_values.size()); + if (count == 1) + ss << StringFromFormat(" float %s;\n", it.first.c_str()); + else + ss << StringFromFormat(" float%u %s;\n", count, it.first.c_str()); + + for (u32 i = count; i < 4; i++) + ss << " float ubo_align_" << unused_counter++ << "_;\n"; + } + } + + ss << "};\n\n"; + return ss.str(); +} + +std::string PostProcessing::GetHeader() const +{ + std::stringstream ss; + ss << GetUniformBufferHeader(); + if (g_ActiveConfig.backend_info.api_type == APIType::D3D) + { + ss << "Texture2DArray samp0 : register(t0);\n"; + ss << "SamplerState samp0_ss : register(s0);\n"; + } + else + { + ss << "SAMPLER_BINDING(0) uniform sampler2DArray samp0;\n"; + ss << "VARYING_LOCATION(0) in float3 v_tex0;\n"; + ss << "FRAGMENT_OUTPUT_LOCATION(0) out float4 ocol0;\n"; + } + + // Rename main, since we need to set up globals + if (g_ActiveConfig.backend_info.api_type == APIType::D3D) + { + ss << R"( +#define main real_main +static float3 v_tex0; +static float4 ocol0; + +// Wrappers for sampling functions. +#define texture(sampler, coords) sampler.Sample(sampler##_ss, coords) +#define textureOffset(sampler, coords, offset) sampler.Sample(sampler##_ss, coords, offset) +)"; + } + + ss << R"( +float4 Sample() { return texture(samp0, float3(v_tex0.xy, float(layer))); } +float4 SampleLocation(float2 location) { return texture(samp0, float3(location, float(layer))); } +float4 SampleLayer(int layer) { return texture(samp0, float3(v_tex0.xy, float(layer))); } +#define SampleOffset(offset) textureOffset(samp0, float3(v_tex0.xy, float(layer)), offset) + +float2 GetResolution() +{ + return resolution.xy; +} + +float2 GetInvResolution() +{ + return resolution.zw; +} + +float2 GetCoordinates() +{ + return v_tex0.xy; +} + +uint GetTime() +{ + return time; +} + +void SetOutput(float4 color) +{ + ocol0 = color; +} + +#define GetOption(x) (x) +#define OptionEnabled(x) ((x) != 0) + +)"; + return ss.str(); +} + +std::string PostProcessing::GetFooter() const +{ + if (g_ActiveConfig.backend_info.api_type == APIType::D3D) + { + return R"( + +#undef main +void main(in float3 v_tex0_ : TEXCOORD0, out float4 ocol0_ : SV_Target) +{ + v_tex0 = v_tex0_; + real_main(); + ocol0_ = ocol0; +})"; + } + else + { + return {}; + } +} + +bool PostProcessing::CompileVertexShader() +{ + std::stringstream ss; + ss << GetUniformBufferHeader(); + + if (g_ActiveConfig.backend_info.api_type == APIType::D3D) + { + ss << "void main(in uint id : SV_VertexID, out float3 v_tex0 : TEXCOORD0,\n"; + ss << " out float4 opos : SV_Position) {\n"; + } + else + { + ss << "VARYING_LOCATION(0) out float3 v_tex0;\n"; + ss << "#define id gl_VertexID\n"; + ss << "#define opos gl_Position\n"; + ss << "void main() {\n"; + } + ss << " v_tex0 = float3(float((id << 1) & 2), float(id & 2), 0.0f);\n"; + ss << " opos = float4(v_tex0.xy * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"; + ss << " v_tex0 = float3(src_rect.xy + (src_rect.zw * v_tex0.xy), 0.0f);\n"; + + if (g_ActiveConfig.backend_info.api_type == APIType::Vulkan) + ss << " opos.y = -opos.y;\n"; + + ss << "}\n"; + + m_vertex_shader = g_renderer->CreateShaderFromSource(ShaderStage::Vertex, ss.str()); + if (!m_vertex_shader) + { + PanicAlert("Failed to compile post-processing vertex shader"); + return false; + } + + return true; +} + +struct BuiltinUniforms +{ + float resolution[4]; + float src_rect[4]; + s32 time; + u32 layer; + u32 padding[2]; +}; + +size_t PostProcessing::CalculateUniformsSize() const +{ + // Allocate a vec4 for each uniform to simplify allocation. + return sizeof(BuiltinUniforms) + m_config.GetOptions().size() * sizeof(float) * 4; +} + +void PostProcessing::FillUniformBuffer(const MathUtil::Rectangle<int>& src, + const AbstractTexture* src_tex, int src_layer) +{ + const float rcp_src_width = 1.0f / src_tex->GetWidth(); + const float rcp_src_height = 1.0f / src_tex->GetHeight(); + BuiltinUniforms builtin_uniforms = { + {static_cast<float>(src_tex->GetWidth()), static_cast<float>(src_tex->GetHeight()), + rcp_src_width, rcp_src_height}, + {static_cast<float>(src.left) * rcp_src_width, static_cast<float>(src.top) * rcp_src_height, + static_cast<float>(src.GetWidth()) * rcp_src_width, + static_cast<float>(src.GetHeight()) * rcp_src_height}, + static_cast<s32>(m_timer.GetTimeElapsed()), + static_cast<u32>(src_layer), + }; + + u8* buf = m_uniform_staging_buffer.data(); + std::memcpy(buf, &builtin_uniforms, sizeof(builtin_uniforms)); + buf += sizeof(builtin_uniforms); + + for (const auto& it : m_config.GetOptions()) + { + union + { + u32 as_bool[4]; + s32 as_int[4]; + float as_float[4]; + } value = {}; + + switch (it.second.m_type) + { + case PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_BOOL: + value.as_bool[0] = it.second.m_bool_value ? 1 : 0; + break; + + case PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER: + ASSERT(it.second.m_integer_values.size() < 4); + std::copy_n(it.second.m_integer_values.begin(), it.second.m_integer_values.size(), + value.as_int); + break; + + case PostProcessingConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT: + ASSERT(it.second.m_float_values.size() < 4); + std::copy_n(it.second.m_float_values.begin(), it.second.m_float_values.size(), + value.as_float); + break; + } + + std::memcpy(buf, &value, sizeof(value)); + buf += sizeof(value); + } +} + +bool PostProcessing::CompilePixelShader() +{ + m_pipeline.reset(); + m_pixel_shader.reset(); + + // Generate GLSL and compile the new shader. + m_config.LoadShader(g_ActiveConfig.sPostProcessingShader); + m_pixel_shader = g_renderer->CreateShaderFromSource( + ShaderStage::Pixel, GetHeader() + m_config.GetShaderCode() + GetFooter()); + if (!m_pixel_shader) + { + PanicAlert("Failed to compile post-processing shader %s", m_config.GetShader().c_str()); + + // Use default shader. + m_config.LoadDefaultShader(); + m_pixel_shader = g_renderer->CreateShaderFromSource( + ShaderStage::Pixel, GetHeader() + m_config.GetShaderCode() + GetFooter()); + if (!m_pixel_shader) + return false; + } + + m_uniform_staging_buffer.resize(CalculateUniformsSize()); + return true; +} + +bool PostProcessing::CompilePipeline() +{ + AbstractPipelineConfig config = {}; + config.vertex_shader = m_vertex_shader.get(); + config.geometry_shader = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer ? + g_shader_cache->GetTexcoordGeometryShader() : + nullptr; + config.pixel_shader = m_pixel_shader.get(); + config.rasterization_state = RenderState::GetNoCullRasterizationState(PrimitiveType::Triangles); + config.depth_state = RenderState::GetNoDepthTestingDepthState(); + config.blending_state = RenderState::GetNoBlendingBlendState(); + config.framebuffer_state = RenderState::GetColorFramebufferState(m_framebuffer_format); + config.usage = AbstractPipelineUsage::Utility; + m_pipeline = g_renderer->CreatePipeline(config); + if (!m_pipeline) + return false; + + return true; +} +} // namespace VideoCommon |
