Vulkan: Implement post-processing backend
No new features, just parity with OpenGL.
This commit is contained in:
parent
a10e8b1ef5
commit
417a4ca206
|
@ -4,6 +4,7 @@ set(SRCS
|
|||
FramebufferManager.cpp
|
||||
ObjectCache.cpp
|
||||
PerfQuery.cpp
|
||||
PostProcessing.cpp
|
||||
RasterFont.cpp
|
||||
Renderer.cpp
|
||||
ShaderCompiler.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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 <sstream>
|
||||
|
||||
#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<int>(src_tex->GetWidth()),
|
||||
static_cast<int>(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<float>(src_tex->GetWidth());
|
||||
float src_height_float = static_cast<float>(src_tex->GetHeight());
|
||||
BuiltinUniforms builtin_uniforms = {
|
||||
{src_width_float, src_height_float, 1.0f / src_width_float, 1.0f / src_height_float},
|
||||
{static_cast<float>(src.left) / src_width_float,
|
||||
static_cast<float>(src.top) / src_height_float,
|
||||
static_cast<float>(src.GetWidth()) / src_width_float,
|
||||
static_cast<float>(src.GetHeight()) / src_height_float},
|
||||
static_cast<u32>(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<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 unused" << unused_counter++ << ";\n";
|
||||
}
|
||||
else if (it.second.m_type ==
|
||||
PostProcessingShaderConfiguration::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 unused" << unused_counter++ << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
ss << "} options;\n\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<VulkanPostProcessing>();
|
||||
if (!static_cast<VulkanPostProcessing*>(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<VulkanPostProcessing*>(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<VulkanPostProcessing*>(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
|
||||
|
|
|
@ -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<Texture2D> m_frame_dump_render_texture;
|
||||
VkFramebuffer m_frame_dump_framebuffer = VK_NULL_HANDLE;
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<ClCompile Include="CommandBufferManager.cpp" />
|
||||
<ClCompile Include="FramebufferManager.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="PostProcessing.cpp" />
|
||||
<ClCompile Include="TextureConverter.cpp" />
|
||||
<ClCompile Include="PerfQuery.cpp" />
|
||||
<ClCompile Include="RasterFont.cpp" />
|
||||
|
@ -77,6 +78,7 @@
|
|||
<ClInclude Include="CommandBufferManager.h" />
|
||||
<ClInclude Include="FramebufferManager.h" />
|
||||
<ClInclude Include="Constants.h" />
|
||||
<ClInclude Include="PostProcessing.h" />
|
||||
<ClInclude Include="TextureConverter.h" />
|
||||
<ClInclude Include="RasterFont.h" />
|
||||
<ClInclude Include="StagingBuffer.h" />
|
||||
|
|
|
@ -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<std::string> GetShaders(const std::string& sub_dir = "")
|
||||
{
|
||||
std::vector<std::string> paths =
|
||||
Common::DoFileSearch({".glsl"}, {File::GetUserPath(D_SHADERS_IDX) + sub_dir,
|
||||
File::GetSysDirectory() + SHADERS_DIR DIR_SEP + sub_dir});
|
||||
std::vector<std::string> 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)
|
||||
|
|
Loading…
Reference in New Issue