From a10e8b1ef52452e3f95fc41f512839825e4f310f Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 21 Apr 2017 23:33:12 +1000 Subject: [PATCH 1/3] VideoCommon: Move the blit methods to the backend class The parameter types will be different for each backend currently, anyway (e.g. textures/render passes/etc). --- Source/Core/VideoBackends/OGL/PostProcessing.h | 4 ++-- Source/Core/VideoBackends/OGL/Render.cpp | 7 ++++--- Source/Core/VideoCommon/PostProcessing.h | 6 +----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/PostProcessing.h b/Source/Core/VideoBackends/OGL/PostProcessing.h index 706b113c22..1c8c8ecf52 100644 --- a/Source/Core/VideoBackends/OGL/PostProcessing.h +++ b/Source/Core/VideoBackends/OGL/PostProcessing.h @@ -23,8 +23,8 @@ public: ~OpenGLPostProcessing(); void BlitFromTexture(TargetRectangle src, TargetRectangle dst, int src_texture, int src_width, - int src_height, int layer) override; - void ApplyShader() override; + int src_height, int layer); + void ApplyShader(); private: bool m_initialized; diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index ec68c14f23..ce58be68cc 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1176,6 +1176,7 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width, int src_height) { + OpenGLPostProcessing* post_processor = static_cast(m_post_processor.get()); if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB) { TargetRectangle leftRc, rightRc; @@ -1186,12 +1187,12 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_t else std::tie(leftRc, rightRc) = ConvertStereoRectangle(dst); - m_post_processor->BlitFromTexture(src, leftRc, src_texture, src_width, src_height, 0); - m_post_processor->BlitFromTexture(src, rightRc, src_texture, src_width, src_height, 1); + post_processor->BlitFromTexture(src, leftRc, src_texture, src_width, src_height, 0); + post_processor->BlitFromTexture(src, rightRc, src_texture, src_width, src_height, 1); } else { - m_post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height); + post_processor->BlitFromTexture(src, dst, src_texture, src_width, src_height, 0); } } diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h index 74628c3e54..02ed1b5f7b 100644 --- a/Source/Core/VideoCommon/PostProcessing.h +++ b/Source/Core/VideoCommon/PostProcessing.h @@ -60,6 +60,7 @@ public: bool IsDirty() { return m_any_options_dirty; } void SetDirty(bool dirty) { m_any_options_dirty = dirty; } bool HasOptions() { return m_options.size() > 0; } + const ConfigMap& GetOptions() const { return m_options; } ConfigMap& GetOptions() { return m_options; } const ConfigurationOption& GetOption(const std::string& option) { return m_options[option]; } // For updating option's values @@ -83,11 +84,6 @@ public: virtual ~PostProcessingShaderImplementation(); PostProcessingShaderConfiguration* GetConfig() { return &m_config; } - // Should be implemented by the backends for backend specific code - virtual void BlitFromTexture(TargetRectangle src, TargetRectangle dst, int src_texture, - int src_width, int src_height, int layer = 0) = 0; - virtual void ApplyShader() = 0; - protected: // Timer for determining our time value Common::Timer m_timer; From 417a4ca206a5cae7cc08c80564181af74c8fa51a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 21 Apr 2017 23:33:58 +1000 Subject: [PATCH 2/3] Vulkan: Implement post-processing backend No new features, just parity with OpenGL. --- .../Core/VideoBackends/Vulkan/CMakeLists.txt | 1 + .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 32 +- .../Core/VideoBackends/Vulkan/ObjectCache.h | 7 + .../VideoBackends/Vulkan/PostProcessing.cpp | 319 ++++++++++++++++++ .../VideoBackends/Vulkan/PostProcessing.h | 45 +++ .../Core/VideoBackends/Vulkan/RasterFont.cpp | 5 + Source/Core/VideoBackends/Vulkan/RasterFont.h | 2 + Source/Core/VideoBackends/Vulkan/Renderer.cpp | 76 ++--- Source/Core/VideoBackends/Vulkan/Renderer.h | 7 +- .../Core/VideoBackends/Vulkan/Vulkan.vcxproj | 2 + .../VideoBackends/Vulkan/VulkanContext.cpp | 23 +- 11 files changed, 445 insertions(+), 74 deletions(-) create mode 100644 Source/Core/VideoBackends/Vulkan/PostProcessing.cpp create mode 100644 Source/Core/VideoBackends/Vulkan/PostProcessing.h diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 8d9f980dd9..1b5ed96f9e 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -4,6 +4,7 @@ set(SRCS FramebufferManager.cpp ObjectCache.cpp PerfQuery.cpp + PostProcessing.cpp RasterFont.cpp Renderer.cpp ShaderCompiler.cpp diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 431728f39e..4fadbfcfc4 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -421,6 +421,23 @@ VkPipeline ObjectCache::GetComputePipeline(const ComputePipelineInfo& info) return pipeline; } +void ObjectCache::ClearPipelineCache() +{ + for (const auto& it : m_pipeline_objects) + { + if (it.second != VK_NULL_HANDLE) + vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr); + } + m_pipeline_objects.clear(); + + for (const auto& it : m_compute_pipeline_objects) + { + if (it.second != VK_NULL_HANDLE) + vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr); + } + m_compute_pipeline_objects.clear(); +} + std::string ObjectCache::GetDiskCacheFileName(const char* type) { return StringFromFormat("%svulkan-%s-%s.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(), @@ -567,20 +584,7 @@ bool ObjectCache::ValidatePipelineCache(const u8* data, size_t data_length) void ObjectCache::DestroyPipelineCache() { - for (const auto& it : m_pipeline_objects) - { - if (it.second != VK_NULL_HANDLE) - vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr); - } - m_pipeline_objects.clear(); - - for (const auto& it : m_compute_pipeline_objects) - { - if (it.second != VK_NULL_HANDLE) - vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr); - } - m_compute_pipeline_objects.clear(); - + ClearPipelineCache(); vkDestroyPipelineCache(g_vulkan_context->GetDevice(), m_pipeline_cache, nullptr); m_pipeline_cache = VK_NULL_HANDLE; } diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.h b/Source/Core/VideoBackends/Vulkan/ObjectCache.h index ac2fe7e962..97c457a7f8 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.h +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.h @@ -137,6 +137,13 @@ public: // Find a pipeline by the specified description, if not found, attempts to create it VkPipeline GetComputePipeline(const ComputePipelineInfo& info); + // Clears our pipeline cache of all objects. This is necessary when recompiling shaders, + // as drivers are free to return the same pointer again, which means that we may end up using + // and old pipeline object if they are not cleared first. Some stutter may be experienced + // while our cache is rebuilt on use, but the pipeline cache object should mitigate this. + // NOTE: Ensure that none of these objects are in use before calling. + void ClearPipelineCache(); + // Saves the pipeline cache to disk. Call when shutting down. void SavePipelineCache(); diff --git a/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp b/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp new file mode 100644 index 0000000000..db8da49499 --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/PostProcessing.cpp @@ -0,0 +1,319 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/Vulkan/PostProcessing.h" +#include + +#include "Common/Assert.h" +#include "Common/StringUtil.h" + +#include "VideoBackends/Vulkan/CommandBufferManager.h" +#include "VideoBackends/Vulkan/ObjectCache.h" +#include "VideoBackends/Vulkan/Texture2D.h" +#include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VulkanContext.h" + +#include "VideoCommon/VideoCommon.h" +#include "VideoCommon/VideoConfig.h" + +namespace Vulkan +{ +VulkanPostProcessing::~VulkanPostProcessing() +{ + if (m_default_fragment_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_default_fragment_shader, nullptr); + if (m_fragment_shader != VK_NULL_HANDLE) + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); +} + +bool VulkanPostProcessing::Initialize(const Texture2D* font_texture) +{ + m_font_texture = font_texture; + if (!CompileDefaultShader()) + return false; + + RecompileShader(); + return true; +} + +void VulkanPostProcessing::BlitFromTexture(const TargetRectangle& dst, const TargetRectangle& src, + const Texture2D* src_tex, int src_layer, + VkRenderPass render_pass) +{ + VkShaderModule fragment_shader = + m_fragment_shader != VK_NULL_HANDLE ? m_fragment_shader : m_default_fragment_shader; + UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), + g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass, + g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, + fragment_shader); + + // Source is always bound. + draw.SetPSSampler(0, src_tex->GetView(), g_object_cache->GetLinearSampler()); + + // No need to allocate uniforms for the default shader. + // The config will also still contain the invalid shader at this point. + if (fragment_shader != m_default_fragment_shader) + { + size_t uniforms_size = CalculateUniformsSize(); + u8* uniforms = draw.AllocatePSUniforms(uniforms_size); + FillUniformBuffer(uniforms, src, src_tex, src_layer); + draw.CommitPSUniforms(uniforms_size); + draw.SetPSSampler(1, m_font_texture->GetView(), g_object_cache->GetLinearSampler()); + } + + draw.DrawQuad(dst.left, dst.top, dst.GetWidth(), dst.GetHeight(), src.left, src.top, src_layer, + src.GetWidth(), src.GetHeight(), static_cast(src_tex->GetWidth()), + static_cast(src_tex->GetHeight())); +} + +struct BuiltinUniforms +{ + float resolution[4]; + float src_rect[4]; + u32 time; + u32 unused[3]; +}; + +size_t VulkanPostProcessing::CalculateUniformsSize() const +{ + // Allocate a vec4 for each uniform to simplify allocation. + return sizeof(BuiltinUniforms) + m_config.GetOptions().size() * sizeof(float) * 4; +} + +void VulkanPostProcessing::FillUniformBuffer(u8* buf, const TargetRectangle& src, + const Texture2D* src_tex, int src_layer) +{ + float src_width_float = static_cast(src_tex->GetWidth()); + float src_height_float = static_cast(src_tex->GetHeight()); + BuiltinUniforms builtin_uniforms = { + {src_width_float, src_height_float, 1.0f / src_width_float, 1.0f / src_height_float}, + {static_cast(src.left) / src_width_float, + static_cast(src.top) / src_height_float, + static_cast(src.GetWidth()) / src_width_float, + static_cast(src.GetHeight()) / src_height_float}, + static_cast(m_timer.GetTimeElapsed())}; + + 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 PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL: + value.as_bool[0] = it.second.m_bool_value ? 1 : 0; + break; + + case PostProcessingShaderConfiguration::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 PostProcessingShaderConfiguration::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); + } +} + +static const std::string DEFAULT_FRAGMENT_SHADER_SOURCE = R"( + layout(set = 1, binding = 0) uniform sampler2DArray samp0; + + layout(location = 0) in float3 uv0; + layout(location = 1) in float4 col0; + layout(location = 0) out float4 ocol0; + + void main() + { + ocol0 = float4(texture(samp0, uv0).xyz, 1.0); + } +)"; + +static const std::string POSTPROCESSING_SHADER_HEADER = R"( + SAMPLER_BINDING(0) uniform sampler2DArray samp0; + SAMPLER_BINDING(1) uniform sampler2D samp1; + + layout(location = 0) in float3 uv0; + layout(location = 1) in float4 col0; + layout(location = 0) out float4 ocol0; + + // Interfacing functions + // The EFB may have a zero alpha value, which we don't want to write to the frame dump, so set it to one here. + float4 Sample() + { + return float4(texture(samp0, uv0).xyz, 1.0); + } + + float4 SampleLocation(float2 location) + { + return float4(texture(samp0, float3(location, uv0.z)).xyz, 1.0); + } + + float4 SampleLayer(int layer) + { + return float4(texture(samp0, float3(uv0.xy, float(layer))).xyz, 1.0); + } + + #define SampleOffset(offset) float4(textureOffset(samp0, uv0, offset).xyz, 1.0) + + float4 SampleFontLocation(float2 location) + { + return texture(samp1, location); + } + + float2 GetResolution() + { + return options.resolution.xy; + } + + float2 GetInvResolution() + { + return options.resolution.zw; + } + + float2 GetCoordinates() + { + return uv0.xy; + } + + uint GetTime() + { + return options.time; + } + + void SetOutput(float4 color) + { + ocol0 = color; + } + + #define GetOption(x) (options.x) + #define OptionEnabled(x) (options.x != 0) + + // Workaround because there is no getter function for src rect/layer. + float4 src_rect = options.src_rect; + int layer = int(uv0.z); +)"; + +void VulkanPostProcessing::UpdateConfig() +{ + if (m_config.GetShader() == g_ActiveConfig.sPostProcessingShader) + return; + + RecompileShader(); +} + +bool VulkanPostProcessing::CompileDefaultShader() +{ + m_default_fragment_shader = Util::CompileAndCreateFragmentShader(DEFAULT_FRAGMENT_SHADER_SOURCE); + if (m_default_fragment_shader == VK_NULL_HANDLE) + { + PanicAlert("Failed to compile default post-processing shader."); + return false; + } + + return true; +} + +bool VulkanPostProcessing::RecompileShader() +{ + // As a driver can return the same new module pointer when destroying a shader and re-compiling, + // we need to wipe out the pipeline cache, otherwise we risk using old pipelines with old shaders. + // We can't just clear a single pipeline, because we don't know which render pass is going to be + // used here either. + if (m_fragment_shader != VK_NULL_HANDLE) + { + g_command_buffer_mgr->WaitForGPUIdle(); + g_object_cache->ClearPipelineCache(); + vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); + m_fragment_shader = VK_NULL_HANDLE; + } + + // If post-processing is disabled, just use the default shader. + // This way we don't need to allocate uniforms. + if (g_ActiveConfig.sPostProcessingShader.empty()) + return true; + + // Generate GLSL and compile the new shader. + std::string main_code = m_config.LoadShader(); + std::string options_code = GetGLSLUniformBlock(); + std::string code = options_code + POSTPROCESSING_SHADER_HEADER + main_code; + m_fragment_shader = Util::CompileAndCreateFragmentShader(code); + if (m_fragment_shader == VK_NULL_HANDLE) + { + // BlitFromTexture will use the default shader as a fallback. + PanicAlert("Failed to compile post-processing shader %s", m_config.GetShader().c_str()); + return false; + } + + return true; +} + +std::string VulkanPostProcessing::GetGLSLUniformBlock() const +{ + std::stringstream ss; + u32 unused_counter = 1; + ss << "UBO_BINDING(std140, 1) uniform PSBlock {\n"; + + // Builtin uniforms + ss << " float4 resolution;\n"; + ss << " float4 src_rect;\n"; + ss << " uint time;\n"; + for (u32 i = 0; i < 3; i++) + ss << " uint unused" << unused_counter++ << ";\n\n"; + + // Custom options/uniforms + for (const auto& it : m_config.GetOptions()) + { + if (it.second.m_type == + PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL) + { + ss << StringFromFormat(" int %s;\n", it.first.c_str()); + for (u32 i = 0; i < 3; i++) + ss << " int unused" << unused_counter++ << ";\n"; + } + else if (it.second.m_type == + PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER) + { + u32 count = static_cast(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 unused" << unused_counter++ << ";\n"; + } + else if (it.second.m_type == + PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT) + { + u32 count = static_cast(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 unused" << unused_counter++ << ";\n"; + } + } + + ss << "} options;\n\n"; + + return ss.str(); +} + +} // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/PostProcessing.h b/Source/Core/VideoBackends/Vulkan/PostProcessing.h new file mode 100644 index 0000000000..9c07c47772 --- /dev/null +++ b/Source/Core/VideoBackends/Vulkan/PostProcessing.h @@ -0,0 +1,45 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "VideoBackends/Vulkan/VulkanContext.h" + +#include "VideoCommon/PostProcessing.h" +#include "VideoCommon/VideoCommon.h" + +namespace Vulkan +{ +class Texture2D; + +class VulkanPostProcessing : public PostProcessingShaderImplementation +{ +public: + VulkanPostProcessing() = default; + ~VulkanPostProcessing(); + + bool Initialize(const Texture2D* font_texture); + + void BlitFromTexture(const TargetRectangle& dst, const TargetRectangle& src, + const Texture2D* src_tex, int src_layer, VkRenderPass render_pass); + + void UpdateConfig(); + +private: + size_t CalculateUniformsSize() const; + void FillUniformBuffer(u8* buf, const TargetRectangle& src, const Texture2D* src_tex, + int src_layer); + + bool CompileDefaultShader(); + bool RecompileShader(); + std::string GetGLSLUniformBlock() const; + + const Texture2D* m_font_texture = nullptr; + VkShaderModule m_fragment_shader = VK_NULL_HANDLE; + VkShaderModule m_default_fragment_shader = VK_NULL_HANDLE; +}; + +} // namespace diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp index 689c0a8195..def32e5050 100644 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.cpp +++ b/Source/Core/VideoBackends/Vulkan/RasterFont.cpp @@ -175,6 +175,11 @@ RasterFont::~RasterFont() vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_fragment_shader, nullptr); } +const Texture2D* RasterFont::GetTexture() const +{ + return m_texture.get(); +} + bool RasterFont::Initialize() { // Create shaders and texture diff --git a/Source/Core/VideoBackends/Vulkan/RasterFont.h b/Source/Core/VideoBackends/Vulkan/RasterFont.h index 5836da2f73..f12cf0f16c 100644 --- a/Source/Core/VideoBackends/Vulkan/RasterFont.h +++ b/Source/Core/VideoBackends/Vulkan/RasterFont.h @@ -21,6 +21,8 @@ public: RasterFont(); ~RasterFont(); + const Texture2D* GetTexture() const; + bool Initialize(); void PrintMultiLineText(VkRenderPass render_pass, const std::string& text, float start_x, diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 13acc6ac2d..94087bdc1f 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -21,6 +21,7 @@ #include "VideoBackends/Vulkan/CommandBufferManager.h" #include "VideoBackends/Vulkan/FramebufferManager.h" #include "VideoBackends/Vulkan/ObjectCache.h" +#include "VideoBackends/Vulkan/PostProcessing.h" #include "VideoBackends/Vulkan/RasterFont.h" #include "VideoBackends/Vulkan/StagingTexture2D.h" #include "VideoBackends/Vulkan/StateTracker.h" @@ -117,6 +118,15 @@ bool Renderer::Initialize() // Ensure all pipelines previously used by the game have been created. StateTracker::GetInstance()->LoadPipelineUIDCache(); + // Initialize post processing. + m_post_processor = std::make_unique(); + if (!static_cast(m_post_processor.get()) + ->Initialize(m_raster_font->GetTexture())) + { + PanicAlert("failed to initialize post processor."); + return false; + } + // Various initialization routines will have executed commands on the command buffer. // Execute what we have done before beginning the first frame. g_command_buffer_mgr->PrepareToSubmitCommandBuffer(); @@ -618,7 +628,7 @@ void Renderer::DrawEFB(VkRenderPass render_pass, const TargetRectangle& target_r VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Copy EFB -> backbuffer - BlitScreen(render_pass, target_rect, scaled_efb_rect, efb_color_texture, true); + BlitScreen(render_pass, target_rect, scaled_efb_rect, efb_color_texture); // Restore the EFB color texture to color attachment ready for rendering the next frame. if (efb_color_texture == FramebufferManager::GetInstance()->GetEFBColorTexture()) @@ -660,7 +670,7 @@ void Renderer::DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& t 2; source_rect.right -= Renderer::EFBToScaledX(fb_stride - fb_width); - BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true); + BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture()); } } @@ -677,7 +687,7 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ TargetRectangle source_rect = xfb_source->sourceRc; TargetRectangle draw_rect = target_rect; source_rect.right -= fb_stride - fb_width; - BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture(), true); + BlitScreen(render_pass, draw_rect, source_rect, xfb_source->GetTexture()->GetTexture()); } } @@ -911,40 +921,21 @@ void Renderer::FlushFrameDump() } void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, - const TargetRectangle& src_rect, const Texture2D* src_tex, - bool linear_filter) + const TargetRectangle& src_rect, const Texture2D* src_tex) { - // We could potentially use vkCmdBlitImage here. - VkSampler sampler = - linear_filter ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler(); - - // Set up common data - UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass, - g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE, - m_blit_fragment_shader); - - draw.SetPSSampler(0, src_tex->GetView(), sampler); - + VulkanPostProcessing* post_processor = static_cast(m_post_processor.get()); if (g_ActiveConfig.iStereoMode == STEREO_SBS || g_ActiveConfig.iStereoMode == STEREO_TAB) { TargetRectangle left_rect; TargetRectangle right_rect; std::tie(left_rect, right_rect) = ConvertStereoRectangle(dst_rect); - draw.DrawQuad(left_rect.left, left_rect.top, left_rect.GetWidth(), left_rect.GetHeight(), - src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - src_tex->GetWidth(), src_tex->GetHeight()); - - draw.DrawQuad(right_rect.left, right_rect.top, right_rect.GetWidth(), right_rect.GetHeight(), - src_rect.left, src_rect.top, 1, src_rect.GetWidth(), src_rect.GetHeight(), - src_tex->GetWidth(), src_tex->GetHeight()); + post_processor->BlitFromTexture(left_rect, src_rect, src_tex, 0, render_pass); + post_processor->BlitFromTexture(right_rect, src_rect, src_tex, 1, render_pass); } else { - draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(), - src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - src_tex->GetWidth(), src_tex->GetHeight()); + post_processor->BlitFromTexture(dst_rect, src_rect, src_tex, 0, render_pass); } } @@ -1182,6 +1173,9 @@ void Renderer::CheckForConfigChanges() // Wipe sampler cache if force texture filtering or anisotropy changes. if (anisotropy_changed || force_texture_filtering_changed) ResetSamplerStates(); + + // Check for a changed post-processing shader and recompile if needed. + static_cast(m_post_processor.get())->UpdateConfig(); } void Renderer::OnSwapChainResized() @@ -1490,33 +1484,10 @@ bool Renderer::CompileShaders() )"; - static const char BLIT_FRAGMENT_SHADER_SOURCE[] = R"( - layout(set = 1, binding = 0) uniform sampler2DArray samp0; - - layout(location = 0) in float3 uv0; - layout(location = 1) in float4 col0; - layout(location = 0) out float4 ocol0; - - void main() - { - ocol0 = float4(texture(samp0, uv0).xyz, 1.0); - } - )"; - - std::string header = g_object_cache->GetUtilityShaderHeader(); - std::string source; - - source = header + CLEAR_FRAGMENT_SHADER_SOURCE; + std::string source = g_object_cache->GetUtilityShaderHeader() + CLEAR_FRAGMENT_SHADER_SOURCE; m_clear_fragment_shader = Util::CompileAndCreateFragmentShader(source); - source = header + BLIT_FRAGMENT_SHADER_SOURCE; - m_blit_fragment_shader = Util::CompileAndCreateFragmentShader(source); - if (m_clear_fragment_shader == VK_NULL_HANDLE || m_blit_fragment_shader == VK_NULL_HANDLE) - { - return false; - } - - return true; + return m_clear_fragment_shader != VK_NULL_HANDLE; } void Renderer::DestroyShaders() @@ -1530,7 +1501,6 @@ void Renderer::DestroyShaders() }; DestroyShader(m_clear_fragment_shader); - DestroyShader(m_blit_fragment_shader); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 5f86489c48..a6f9400560 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -136,7 +136,7 @@ private: // Copies/scales an image to the currently-bound framebuffer. void BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_rect, - const TargetRectangle& src_rect, const Texture2D* src_tex, bool linear_filter); + const TargetRectangle& src_rect, const Texture2D* src_tex); bool ResizeFrameDumpBuffer(u32 new_width, u32 new_height); void DestroyFrameDumpResources(); @@ -154,11 +154,6 @@ private: // Shaders used for clear/blit. VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE; - // NOTE: The blit shader here is used for the final copy from the source buffer(s) to the swap - // chain buffer for presentation. It ignores the alpha channel of the input image and sets the - // alpha channel to 1.0 to avoid issues with frame dumping and screenshots. - VkShaderModule m_blit_fragment_shader = VK_NULL_HANDLE; - // Texture used for screenshot/frame dumping std::unique_ptr m_frame_dump_render_texture; VkFramebuffer m_frame_dump_framebuffer = VK_NULL_HANDLE; diff --git a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj index c7bdada31b..ed5d15d5ae 100644 --- a/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj +++ b/Source/Core/VideoBackends/Vulkan/Vulkan.vcxproj @@ -53,6 +53,7 @@ + @@ -77,6 +78,7 @@ + diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index b7ef583786..d9cd116566 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -6,6 +6,9 @@ #include "Common/Assert.h" #include "Common/CommonFuncs.h" +#include "Common/CommonPaths.h" +#include "Common/FileSearch.h" +#include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -222,6 +225,21 @@ VulkanContext::GPUList VulkanContext::EnumerateGPUs(VkInstance instance) return gpus; } +static std::vector GetShaders(const std::string& sub_dir = "") +{ + std::vector paths = + Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir, + File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}); + std::vector result; + for (std::string path : paths) + { + std::string name; + SplitPath(path, nullptr, &name, nullptr); + result.push_back(name); + } + return result; +} + void VulkanContext::PopulateBackendInfo(VideoConfig* config) { config->backend_info.api_type = APIType::Vulkan; @@ -237,7 +255,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config) config->backend_info.bSupportsComputeShaders = true; // Assumed support. config->backend_info.bSupportsGPUTextureDecoding = true; // Assumed support. config->backend_info.bSupportsInternalResolutionFrameDumps = true; // Assumed support. - config->backend_info.bSupportsPostProcessing = false; // No support yet. + config->backend_info.bSupportsPostProcessing = true; // Assumed support. config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features. config->backend_info.bSupportsGeometryShaders = false; // Dependent on features. config->backend_info.bSupportsGSInstancing = false; // Dependent on features. @@ -246,6 +264,9 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config) config->backend_info.bSupportsSSAA = false; // Dependent on features. config->backend_info.bSupportsDepthClamp = false; // Dependent on features. config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs. + + g_Config.backend_info.PPShaders = GetShaders(""); + g_Config.backend_info.AnaglyphShaders = GetShaders(ANAGLYPH_DIR DIR_SEP); } void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list) From 27ae5b8d34675244af689b260f0dbb391bb6209e Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 21 Apr 2017 23:59:45 +1000 Subject: [PATCH 3/3] VideoConfigDiag: Move post-processing shader list to post processor The backends don't use this list at all, and since more than one backend supports post-processing now, it's duplicate code. --- Source/Core/DolphinWX/VideoConfigDiag.cpp | 7 ++-- Source/Core/VideoBackends/D3D/main.cpp | 4 --- Source/Core/VideoBackends/D3D12/main.cpp | 4 --- .../Core/VideoBackends/Null/NullBackend.cpp | 2 -- Source/Core/VideoBackends/OGL/main.cpp | 21 ----------- .../VideoBackends/Vulkan/VulkanContext.cpp | 21 ----------- Source/Core/VideoCommon/PostProcessing.cpp | 35 +++++++++++++++++++ Source/Core/VideoCommon/PostProcessing.h | 3 ++ Source/Core/VideoCommon/VideoConfig.h | 2 -- 9 files changed, 42 insertions(+), 57 deletions(-) diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 6f4ae390d2..2efc80c7e0 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -1253,9 +1253,10 @@ void VideoConfigDiag::CreateDescriptionArea(wxPanel* const page, wxBoxSizer* con void VideoConfigDiag::PopulatePostProcessingShaders() { - std::vector& shaders = (vconfig.iStereoMode == STEREO_ANAGLYPH) ? - vconfig.backend_info.AnaglyphShaders : - vconfig.backend_info.PPShaders; + std::vector shaders = + vconfig.iStereoMode == STEREO_ANAGLYPH ? + PostProcessingShaderImplementation::GetAnaglyphShaderList(vconfig.backend_info.api_type) : + PostProcessingShaderImplementation::GetShaderList(vconfig.backend_info.api_type); if (shaders.empty()) return; diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index bc0af68180..48c7dd8391 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -128,10 +128,6 @@ void VideoBackend::InitBackendInfo() } factory->Release(); - // Clear ppshaders string vector - g_Config.backend_info.PPShaders.clear(); - g_Config.backend_info.AnaglyphShaders.clear(); - DX11::D3D::UnloadDXGI(); DX11::D3D::UnloadD3D(); } diff --git a/Source/Core/VideoBackends/D3D12/main.cpp b/Source/Core/VideoBackends/D3D12/main.cpp index 2c95bff055..cb075fa071 100644 --- a/Source/Core/VideoBackends/D3D12/main.cpp +++ b/Source/Core/VideoBackends/D3D12/main.cpp @@ -140,10 +140,6 @@ void VideoBackend::InitBackendInfo() } factory->Release(); - // Clear ppshaders string vector - g_Config.backend_info.PPShaders.clear(); - g_Config.backend_info.AnaglyphShaders.clear(); - D3D::UnloadD3D(); D3D::UnloadDXGI(); } diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index 60a6f637ac..7892b86927 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -49,8 +49,6 @@ void VideoBackend::InitBackendInfo() // aamodes: We only support 1 sample, so no MSAA g_Config.backend_info.Adapters.clear(); g_Config.backend_info.AAModes = {1}; - g_Config.backend_info.PPShaders.clear(); - g_Config.backend_info.AnaglyphShaders.clear(); } bool VideoBackend::Initialize(void* window_handle) diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 868d789785..430859932e 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -38,8 +38,6 @@ Make AA apply instantly during gameplay if possible #include #include -#include "Common/CommonPaths.h" -#include "Common/FileSearch.h" #include "Common/GL/GLInterfaceBase.h" #include "Common/GL/GLUtil.h" #include "Common/MsgHandler.h" @@ -79,21 +77,6 @@ std::string VideoBackend::GetDisplayName() const return "OpenGL"; } -static std::vector GetShaders(const std::string& sub_dir = "") -{ - std::vector paths = - Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir, - File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}); - std::vector result; - for (std::string path : paths) - { - std::string name; - SplitPath(path, nullptr, &name, nullptr); - result.push_back(name); - } - return result; -} - void VideoBackend::InitBackendInfo() { g_Config.backend_info.api_type = APIType::OpenGL; @@ -125,10 +108,6 @@ void VideoBackend::InitBackendInfo() // aamodes - 1 is to stay consistent with D3D (means no AA) g_Config.backend_info.AAModes = {1, 2, 4, 8}; - - // pp shaders - g_Config.backend_info.PPShaders = GetShaders(""); - g_Config.backend_info.AnaglyphShaders = GetShaders(ANAGLYPH_DIR DIR_SEP); } bool VideoBackend::InitializeGLExtensions() diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index d9cd116566..58b10dd92e 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -6,9 +6,6 @@ #include "Common/Assert.h" #include "Common/CommonFuncs.h" -#include "Common/CommonPaths.h" -#include "Common/FileSearch.h" -#include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -225,21 +222,6 @@ VulkanContext::GPUList VulkanContext::EnumerateGPUs(VkInstance instance) return gpus; } -static std::vector GetShaders(const std::string& sub_dir = "") -{ - std::vector paths = - Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir, - File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}); - std::vector result; - for (std::string path : paths) - { - std::string name; - SplitPath(path, nullptr, &name, nullptr); - result.push_back(name); - } - return result; -} - void VulkanContext::PopulateBackendInfo(VideoConfig* config) { config->backend_info.api_type = APIType::Vulkan; @@ -264,9 +246,6 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config) config->backend_info.bSupportsSSAA = false; // Dependent on features. config->backend_info.bSupportsDepthClamp = false; // Dependent on features. config->backend_info.bSupportsReversedDepthRange = false; // No support yet due to driver bugs. - - g_Config.backend_info.PPShaders = GetShaders(""); - g_Config.backend_info.AnaglyphShaders = GetShaders(ANAGLYPH_DIR DIR_SEP); } void VulkanContext::PopulateBackendInfoAdapters(VideoConfig* config, const GPUList& gpu_list) diff --git a/Source/Core/VideoCommon/PostProcessing.cpp b/Source/Core/VideoCommon/PostProcessing.cpp index 023b6ef8bc..71f38732e7 100644 --- a/Source/Core/VideoCommon/PostProcessing.cpp +++ b/Source/Core/VideoCommon/PostProcessing.cpp @@ -7,6 +7,7 @@ #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" @@ -27,6 +28,40 @@ PostProcessingShaderImplementation::~PostProcessingShaderImplementation() m_timer.Stop(); } +static std::vector GetShaders(const std::string& sub_dir = "") +{ + std::vector paths = + Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir, + File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir}); + std::vector result; + for (std::string path : paths) + { + std::string name; + SplitPath(path, nullptr, &name, nullptr); + result.push_back(name); + } + return result; +} + +std::vector PostProcessingShaderImplementation::GetShaderList(APIType api_type) +{ + // 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 PostProcessingShaderImplementation::GetAnaglyphShaderList(APIType api_type) +{ + if (api_type == APIType::OpenGL || api_type == APIType::Vulkan) + return GetShaders(ANAGLYPH_DIR DIR_SEP); + + return {}; +} + std::string PostProcessingShaderConfiguration::LoadShader(std::string shader) { // Load the shader from the configuration if there isn't one sent to us. diff --git a/Source/Core/VideoCommon/PostProcessing.h b/Source/Core/VideoCommon/PostProcessing.h index 02ed1b5f7b..4a51e66842 100644 --- a/Source/Core/VideoCommon/PostProcessing.h +++ b/Source/Core/VideoCommon/PostProcessing.h @@ -83,6 +83,9 @@ public: PostProcessingShaderImplementation(); virtual ~PostProcessingShaderImplementation(); + static std::vector GetShaderList(APIType api_type); + static std::vector GetAnaglyphShaderList(APIType api_type); + PostProcessingShaderConfiguration* GetConfig() { return &m_config; } protected: // Timer for determining our time value diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index c628722a3a..eacb30d8b1 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -170,8 +170,6 @@ struct VideoConfig final std::vector Adapters; // for D3D std::vector AAModes; - std::vector PPShaders; // post-processing shaders - std::vector AnaglyphShaders; // anaglyph shaders // TODO: merge AdapterName and Adapters array std::string AdapterName; // for OpenGL