Move most backend functionality to VideoCommon

This commit is contained in:
Stenzek
2019-02-15 11:59:50 +10:00
parent 933f3ba008
commit f039149198
182 changed files with 8334 additions and 15917 deletions

View File

@ -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"
namespace VideoCommon
{
static const char s_default_shader[] = "void main() { SetOutput(Sample()); }\n";
PostProcessingShaderImplementation::PostProcessingShaderImplementation()
{
m_timer.Start();
}
PostProcessingConfiguration::PostProcessingConfiguration() = default;
PostProcessingShaderImplementation::~PostProcessingShaderImplementation()
{
m_timer.Stop();
}
PostProcessingConfiguration::~PostProcessingConfiguration() = default;
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)
{
// 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 {};
}
PostProcessingShaderConfiguration::PostProcessingShaderConfiguration() = default;
PostProcessingShaderConfiguration::~PostProcessingShaderConfiguration() = 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();
return code;
m_current_shader_code = code;
}
void PostProcessingShaderConfiguration::LoadOptions(const std::string& code)
void PostProcessingConfiguration::LoadDefaultShader()
{
m_options.clear();
m_any_options_dirty = false;
m_current_shader_code = s_default_shader;
}
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