diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5994347cd..2151d26b3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -71,6 +71,8 @@ add_library(core
save_state_version.h
settings.cpp
settings.h
+ shadergen.cpp
+ shadergen.h
sio.cpp
sio.h
spu.cpp
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index e1fe31cd2..706bb946a 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -85,6 +85,7 @@
+
@@ -134,6 +135,7 @@
+
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index e928a6f1c..ae512e65e 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -47,6 +47,7 @@
+
@@ -97,5 +98,6 @@
+
\ No newline at end of file
diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp
index 460478a13..d0050c8d7 100644
--- a/src/core/gpu_hw_shadergen.cpp
+++ b/src/core/gpu_hw_shadergen.cpp
@@ -5,174 +5,15 @@
#include
Log_SetChannel(GPU_HW_ShaderGen);
-GPU_HW_ShaderGen::GPU_HW_ShaderGen(HostDisplay::RenderAPI render_api, u32 resolution_scale, bool true_color,
- bool scaled_dithering, GPUTextureFilter texture_filtering, bool uv_limits,
- bool supports_dual_source_blend)
- : m_render_api(render_api), m_resolution_scale(resolution_scale), m_true_color(true_color),
- m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering), m_uv_limits(uv_limits),
- m_glsl(render_api != HostDisplay::RenderAPI::D3D11), m_supports_dual_source_blend(supports_dual_source_blend),
- m_use_glsl_interface_blocks(false)
+GPU_HW_ShaderGen::GPU_HW_ShaderGen(HostDisplay::RenderAPI render_api, u32 resolution_scale, bool true_color, bool scaled_dithering, GPUTextureFilter texture_filtering, bool uv_limits, bool supports_dual_source_blend) :
+ ShaderGen(render_api, supports_dual_source_blend),
+ m_resolution_scale(resolution_scale), m_true_color(true_color),
+ m_scaled_dithering(scaled_dithering), m_texture_filter(texture_filtering), m_uv_limits(uv_limits)
{
- if (m_glsl)
- {
- if (m_render_api == HostDisplay::RenderAPI::OpenGL || m_render_api == HostDisplay::RenderAPI::OpenGLES)
- SetGLSLVersionString();
-
- m_use_glsl_interface_blocks = (IsVulkan() || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_VERSION_3_2);
- m_use_glsl_binding_layout = (IsVulkan() || UseGLSLBindingLayout());
- }
}
GPU_HW_ShaderGen::~GPU_HW_ShaderGen() = default;
-bool GPU_HW_ShaderGen::UseGLSLBindingLayout()
-{
- return (GLAD_GL_ES_VERSION_3_1 || GLAD_GL_VERSION_4_2 ||
- (GLAD_GL_ARB_explicit_attrib_location && GLAD_GL_ARB_explicit_uniform_location &&
- GLAD_GL_ARB_shading_language_420pack));
-}
-
-static void DefineMacro(std::stringstream& ss, const char* name, bool enabled)
-{
- ss << "#define " << name << " " << BoolToUInt32(enabled) << "\n";
-}
-
-void GPU_HW_ShaderGen::SetGLSLVersionString()
-{
- const char* glsl_version = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION));
- const bool glsl_es = (m_render_api == HostDisplay::RenderAPI::OpenGLES);
- Assert(glsl_version != nullptr);
-
- // Skip any strings in front of the version code.
- const char* glsl_version_start = glsl_version;
- while (*glsl_version_start != '\0' && (*glsl_version_start < '0' || *glsl_version_start > '9'))
- glsl_version_start++;
-
- int major_version = 0, minor_version = 0;
- if (std::sscanf(glsl_version_start, "%d.%d", &major_version, &minor_version) == 2)
- {
- // Cap at GLSL 4.3, we're not using anything newer for now.
- if (!glsl_es && (major_version > 4 || (major_version == 4 && minor_version > 30)))
- {
- major_version = 4;
- minor_version = 30;
- }
- else if (glsl_es && (major_version > 3 || (major_version == 3 && minor_version > 20)))
- {
- major_version = 3;
- minor_version = 20;
- }
- }
- else
- {
- Log_ErrorPrintf("Invalid GLSL version string: '%s' ('%s')", glsl_version, glsl_version_start);
- if (glsl_es)
- {
- major_version = 3;
- minor_version = 0;
- }
- m_glsl_version_string = glsl_es ? "300" : "130";
- }
-
- char buf[128];
- std::snprintf(buf, sizeof(buf), "#version %d%02d%s", major_version, minor_version,
- (glsl_es && major_version >= 3) ? " es" : "");
- m_glsl_version_string = buf;
-}
-
-void GPU_HW_ShaderGen::WriteHeader(std::stringstream& ss)
-{
- if (m_render_api == HostDisplay::RenderAPI::OpenGL || m_render_api == HostDisplay::RenderAPI::OpenGLES)
- ss << m_glsl_version_string << "\n\n";
- else if (m_render_api == HostDisplay::RenderAPI::Vulkan)
- ss << "#version 450 core\n\n";
-
- // Extension enabling for OpenGL.
- if (m_render_api == HostDisplay::RenderAPI::OpenGLES)
- {
- // Enable EXT_blend_func_extended for dual-source blend on OpenGL ES.
- if (GLAD_GL_EXT_blend_func_extended)
- ss << "#extension GL_EXT_blend_func_extended : require\n";
- }
- else if (m_render_api == HostDisplay::RenderAPI::OpenGL)
- {
- // Need extensions for binding layout if GL<4.3.
- if (m_use_glsl_binding_layout && !GLAD_GL_VERSION_4_3)
- {
- ss << "#extension GL_ARB_explicit_attrib_location : require\n";
- ss << "#extension GL_ARB_explicit_uniform_location : require\n";
- ss << "#extension GL_ARB_shading_language_420pack : require\n";
- }
-
- if (!GLAD_GL_VERSION_3_2)
- ss << "#extension GL_ARB_uniform_buffer_object : require\n";
-
- // Enable SSBOs if it's not required by the version.
- if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ES_VERSION_3_1 && GLAD_GL_ARB_shader_storage_buffer_object)
- ss << "#extension GL_ARB_shader_storage_buffer_object : require\n";
- }
-
- DefineMacro(ss, "API_OPENGL", m_render_api == HostDisplay::RenderAPI::OpenGL);
- DefineMacro(ss, "API_OPENGL_ES", m_render_api == HostDisplay::RenderAPI::OpenGLES);
- DefineMacro(ss, "API_D3D11", m_render_api == HostDisplay::RenderAPI::D3D11);
- DefineMacro(ss, "API_VULKAN", m_render_api == HostDisplay::RenderAPI::Vulkan);
-
- if (m_render_api == HostDisplay::RenderAPI::OpenGLES)
- {
- ss << "precision highp float;\n";
- ss << "precision highp int;\n";
- ss << "precision highp sampler2D;\n";
-
- if (GLAD_GL_ES_VERSION_3_2)
- ss << "precision highp usamplerBuffer;\n";
-
- ss << "\n";
- }
-
- if (m_glsl)
- {
- ss << "#define GLSL 1\n";
- ss << "#define float2 vec2\n";
- ss << "#define float3 vec3\n";
- ss << "#define float4 vec4\n";
- ss << "#define int2 ivec2\n";
- ss << "#define int3 ivec3\n";
- ss << "#define int4 ivec4\n";
- ss << "#define uint2 uvec2\n";
- ss << "#define uint3 uvec3\n";
- ss << "#define uint4 uvec4\n";
- ss << "#define nointerpolation flat\n";
- ss << "#define frac fract\n";
- ss << "#define lerp mix\n";
-
- ss << "#define CONSTANT const\n";
- ss << "#define VECTOR_EQ(a, b) ((a) == (b))\n";
- ss << "#define VECTOR_NEQ(a, b) ((a) != (b))\n";
- ss << "#define VECTOR_COMP_EQ(a, b) equal((a), (b))\n";
- ss << "#define VECTOR_COMP_NEQ(a, b) notEqual((a), (b))\n";
- ss << "#define SAMPLE_TEXTURE(name, coords) texture(name, coords)\n";
- ss << "#define LOAD_TEXTURE(name, coords, mip) texelFetch(name, coords, mip)\n";
- ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) texelFetchOffset(name, coords, mip, offset)\n";
- ss << "#define LOAD_TEXTURE_BUFFER(name, index) texelFetch(name, index)\n";
- }
- else
- {
- ss << "#define HLSL 1\n";
- ss << "#define roundEven round\n";
- ss << "#define CONSTANT static const\n";
- ss << "#define VECTOR_EQ(a, b) (all((a) == (b)))\n";
- ss << "#define VECTOR_NEQ(a, b) (any((a) != (b)))\n";
- ss << "#define VECTOR_COMP_EQ(a, b) ((a) == (b))\n";
- ss << "#define VECTOR_COMP_NEQ(a, b) ((a) != (b))\n";
- ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n";
- ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
- ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) name.Load(int3(coords, mip), offset)\n";
- ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(index)\n";
- }
-
- ss << "\n";
-}
-
void GPU_HW_ShaderGen::WriteCommonFunctions(std::stringstream& ss)
{
ss << "CONSTANT uint RESOLUTION_SCALE = " << m_resolution_scale << "u;\n";
@@ -224,272 +65,6 @@ float4 RGBA5551ToRGBA8(uint v)
)";
}
-void GPU_HW_ShaderGen::DeclareUniformBuffer(std::stringstream& ss, const std::initializer_list& members,
- bool push_constant_on_vulkan)
-{
- if (IsVulkan())
- {
- if (push_constant_on_vulkan)
- ss << "layout(push_constant) uniform PushConstants\n";
- else
- ss << "layout(std140, set = 0, binding = 0) uniform UBOBlock\n";
- }
- else if (m_glsl)
- {
- if (m_use_glsl_binding_layout)
- ss << "layout(std140, binding = 1) uniform UBOBlock\n";
- else
- ss << "layout(std140) uniform UBOBlock\n";
- }
- else
- {
- ss << "cbuffer UBOBlock : register(b0)\n";
- }
-
- ss << "{\n";
- for (const char* member : members)
- ss << member << ";\n";
- ss << "};\n\n";
-}
-
-void GPU_HW_ShaderGen::DeclareTexture(std::stringstream& ss, const char* name, u32 index)
-{
- if (m_glsl)
- {
- if (IsVulkan())
- ss << "layout(set = 0, binding = " << (index + 1u) << ") ";
- else if (m_use_glsl_binding_layout)
- ss << "layout(binding = " << index << ") ";
-
- ss << "uniform sampler2D " << name << ";\n";
- }
- else
- {
- ss << "Texture2D " << name << " : register(t" << index << ");\n";
- ss << "SamplerState " << name << "_ss : register(s" << index << ");\n";
- }
-}
-
-void GPU_HW_ShaderGen::DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int,
- bool is_unsigned)
-{
- if (m_glsl)
- {
- if (IsVulkan())
- ss << "layout(set = 0, binding = " << index << ") ";
- else if (m_use_glsl_binding_layout)
- ss << "layout(binding = " << index << ") ";
-
- ss << "uniform " << (is_int ? (is_unsigned ? "u" : "i") : "") << "samplerBuffer " << name << ";\n";
- }
- else
- {
- ss << "Buffer<" << (is_int ? (is_unsigned ? "uint4" : "int4") : "float4") << "> " << name << " : register(t"
- << index << ");\n";
- }
-}
-
-void GPU_HW_ShaderGen::DeclareVertexEntryPoint(
- std::stringstream& ss, const std::initializer_list& attributes, u32 num_color_outputs,
- u32 num_texcoord_outputs, const std::initializer_list>& additional_outputs,
- bool declare_vertex_id, const char* output_block_suffix)
-{
- if (m_glsl)
- {
- if (m_use_glsl_binding_layout)
- {
- u32 attribute_counter = 0;
- for (const char* attribute : attributes)
- {
- ss << "layout(location = " << attribute_counter << ") in " << attribute << ";\n";
- attribute_counter++;
- }
- }
- else
- {
- for (const char* attribute : attributes)
- ss << "in " << attribute << ";\n";
- }
-
- if (m_use_glsl_interface_blocks)
- {
- if (IsVulkan())
- ss << "layout(location = 0) ";
-
- ss << "out VertexData" << output_block_suffix << " {\n";
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << " float4 v_col" << i << ";\n";
-
- for (u32 i = 0; i < num_texcoord_outputs; i++)
- ss << " float2 v_tex" << i << ";\n";
-
- for (const auto [qualifiers, name] : additional_outputs)
- ss << " " << qualifiers << " " << name << ";\n";
- ss << "};\n";
- }
- else
- {
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << "out float4 v_col" << i << ";\n";
-
- for (u32 i = 0; i < num_texcoord_outputs; i++)
- ss << "out float2 v_tex" << i << ";\n";
-
- for (const auto [qualifiers, name] : additional_outputs)
- ss << qualifiers << " out " << name << ";\n";
- }
-
- ss << "#define v_pos gl_Position\n\n";
- if (declare_vertex_id)
- {
- if (IsVulkan())
- ss << "#define v_id uint(gl_VertexIndex)\n";
- else
- ss << "#define v_id uint(gl_VertexID)\n";
- }
-
- ss << "\n";
- ss << "void main()\n";
- }
- else
- {
- ss << "void main(\n";
-
- if (declare_vertex_id)
- ss << " in uint v_id : SV_VertexID,\n";
-
- u32 attribute_counter = 0;
- for (const char* attribute : attributes)
- {
- ss << " in " << attribute << " : ATTR" << attribute_counter << ",\n";
- attribute_counter++;
- }
-
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << " out float4 v_col" << i << " : COLOR" << i << ",\n";
-
- for (u32 i = 0; i < num_texcoord_outputs; i++)
- ss << " out float2 v_tex" << i << " : TEXCOORD" << i << ",\n";
-
- u32 additional_counter = num_texcoord_outputs;
- for (const auto [qualifiers, name] : additional_outputs)
- {
- ss << " " << qualifiers << " out " << name << " : TEXCOORD" << additional_counter << ",\n";
- additional_counter++;
- }
-
- ss << " out float4 v_pos : SV_Position)\n";
- }
-}
-
-void GPU_HW_ShaderGen::DeclareFragmentEntryPoint(
- std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
- const std::initializer_list>& additional_inputs,
- bool declare_fragcoord /* = false */, u32 num_color_outputs /* = 1 */, bool depth_output /* = false */)
-{
- if (m_glsl)
- {
- if (m_use_glsl_interface_blocks)
- {
- if (IsVulkan())
- ss << "layout(location = 0) ";
-
- ss << "in VertexData {\n";
- for (u32 i = 0; i < num_color_inputs; i++)
- ss << " float4 v_col" << i << ";\n";
-
- for (u32 i = 0; i < num_texcoord_inputs; i++)
- ss << " float2 v_tex" << i << ";\n";
-
- for (const auto [qualifiers, name] : additional_inputs)
- ss << " " << qualifiers << " " << name << ";\n";
- ss << "};\n";
- }
- else
- {
- for (u32 i = 0; i < num_color_inputs; i++)
- ss << "in float4 v_col" << i << ";\n";
-
- for (u32 i = 0; i < num_texcoord_inputs; i++)
- ss << "in float2 v_tex" << i << ";\n";
-
- for (const auto [qualifiers, name] : additional_inputs)
- ss << qualifiers << " in " << name << ";\n";
- }
-
- if (declare_fragcoord)
- ss << "#define v_pos gl_FragCoord\n";
-
- if (depth_output)
- ss << "#define o_depth gl_FragDepth\n";
-
- if (m_use_glsl_binding_layout)
- {
- if (m_supports_dual_source_blend)
- {
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << "layout(location = 0, index = " << i << ") out float4 o_col" << i << ";\n";
- }
- else
- {
- Assert(num_color_outputs <= 1);
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << "layout(location = 0" << i << ") out float4 o_col" << i << ";\n";
- }
- }
- else
- {
- for (u32 i = 0; i < num_color_outputs; i++)
- ss << "out float4 o_col" << i << ";\n";
- }
-
- ss << "\n";
-
- ss << "void main()\n";
- }
- else
- {
- {
- ss << "void main(\n";
-
- for (u32 i = 0; i < num_color_inputs; i++)
- ss << " in float4 v_col" << i << " : COLOR" << i << ",\n";
-
- for (u32 i = 0; i < num_texcoord_inputs; i++)
- ss << " in float2 v_tex" << i << " : TEXCOORD" << i << ",\n";
-
- u32 additional_counter = num_texcoord_inputs;
- for (const auto [qualifiers, name] : additional_inputs)
- {
- ss << " " << qualifiers << " in " << name << " : TEXCOORD" << additional_counter << ",\n";
- additional_counter++;
- }
-
- if (declare_fragcoord)
- ss << " in float4 v_pos : SV_Position,\n";
-
- if (depth_output)
- {
- ss << " out float o_depth : SV_Depth";
- if (num_color_outputs > 0)
- ss << ",\n";
- else
- ss << ")\n";
- }
-
- for (u32 i = 0; i < num_color_outputs; i++)
- {
- ss << " out float4 o_col" << i << " : SV_Target" << i;
-
- if (i == (num_color_outputs - 1))
- ss << ")\n";
- else
- ss << ",\n";
- }
- }
- }
-}
-
void GPU_HW_ShaderGen::WriteBatchUniformBuffer(std::stringstream& ss)
{
DeclareUniformBuffer(ss,
@@ -1371,41 +946,6 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
return ss.str();
}
-std::string GPU_HW_ShaderGen::GenerateScreenQuadVertexShader()
-{
- std::stringstream ss;
- WriteHeader(ss);
- DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
- ss << R"(
-{
- v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
- v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
- #if API_OPENGL || API_OPENGL_ES || API_VULKAN
- v_pos.y = -v_pos.y;
- #endif
-}
-)";
-
- return ss.str();
-}
-
-std::string GPU_HW_ShaderGen::GenerateFillFragmentShader()
-{
- std::stringstream ss;
- WriteHeader(ss);
- DeclareUniformBuffer(ss, {"float4 u_fill_color"}, true);
- DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1, true);
-
- ss << R"(
-{
- o_col0 = u_fill_color;
- o_depth = u_fill_color.a;
-}
-)";
-
- return ss.str();
-}
-
std::string GPU_HW_ShaderGen::GenerateInterlacedFillFragmentShader()
{
std::stringstream ss;
@@ -1427,24 +967,6 @@ std::string GPU_HW_ShaderGen::GenerateInterlacedFillFragmentShader()
return ss.str();
}
-std::string GPU_HW_ShaderGen::GenerateCopyFragmentShader()
-{
- std::stringstream ss;
- WriteHeader(ss);
- DeclareUniformBuffer(ss, {"float4 u_src_rect"}, true);
- DeclareTexture(ss, "samp0", 0);
- DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1);
-
- ss << R"(
-{
- float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw;
- o_col0 = SAMPLE_TEXTURE(samp0, coords);
-}
-)";
-
- return ss.str();
-}
-
std::string GPU_HW_ShaderGen::GenerateDisplayFragmentShader(bool depth_24bit,
GPU_HW::InterlacedRenderMode interlace_mode)
{
diff --git a/src/core/gpu_hw_shadergen.h b/src/core/gpu_hw_shadergen.h
index c2c399404..a881d4b41 100644
--- a/src/core/gpu_hw_shadergen.h
+++ b/src/core/gpu_hw_shadergen.h
@@ -1,25 +1,18 @@
#pragma once
#include "gpu_hw.h"
-#include "host_display.h"
-#include
-#include
+#include "shadergen.h"
-class GPU_HW_ShaderGen
+class GPU_HW_ShaderGen : public ShaderGen
{
public:
GPU_HW_ShaderGen(HostDisplay::RenderAPI render_api, u32 resolution_scale, bool true_color, bool scaled_dithering,
GPUTextureFilter texture_filtering, bool uv_limits, bool supports_dual_source_blend);
~GPU_HW_ShaderGen();
- static bool UseGLSLBindingLayout();
-
std::string GenerateBatchVertexShader(bool textured);
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency, GPU::TextureMode texture_mode,
bool dithering, bool interlacing);
- std::string GenerateScreenQuadVertexShader();
- std::string GenerateFillFragmentShader();
std::string GenerateInterlacedFillFragmentShader();
- std::string GenerateCopyFragmentShader();
std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode);
std::string GenerateVRAMReadFragmentShader();
std::string GenerateVRAMWriteFragmentShader(bool use_ssbo);
@@ -27,36 +20,13 @@ public:
std::string GenerateVRAMUpdateDepthFragmentShader();
private:
- ALWAYS_INLINE bool IsVulkan() const { return (m_render_api == HostDisplay::RenderAPI::Vulkan); }
-
- void SetGLSLVersionString();
- void WriteHeader(std::stringstream& ss);
- void DeclareUniformBuffer(std::stringstream& ss, const std::initializer_list& members,
- bool push_constant_on_vulkan);
- void DeclareTexture(std::stringstream& ss, const char* name, u32 index);
- void DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned);
- void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list& attributes,
- u32 num_color_outputs, u32 num_texcoord_outputs,
- const std::initializer_list>& additional_outputs,
- bool declare_vertex_id = false, const char* output_block_suffix = "");
- void DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
- const std::initializer_list>& additional_inputs,
- bool declare_fragcoord = false, u32 num_color_outputs = 1, bool depth_output = false);
-
void WriteCommonFunctions(std::stringstream& ss);
void WriteBatchUniformBuffer(std::stringstream& ss);
void WriteBatchTextureFilter(std::stringstream& ss, GPUTextureFilter texture_filter);
- HostDisplay::RenderAPI m_render_api;
u32 m_resolution_scale;
bool m_true_color;
bool m_scaled_dithering;
GPUTextureFilter m_texture_filter;
bool m_uv_limits;
- bool m_glsl;
- bool m_supports_dual_source_blend;
- bool m_use_glsl_interface_blocks;
- bool m_use_glsl_binding_layout;
-
- std::string m_glsl_version_string;
};
diff --git a/src/core/shadergen.cpp b/src/core/shadergen.cpp
new file mode 100644
index 000000000..bb4d74439
--- /dev/null
+++ b/src/core/shadergen.cpp
@@ -0,0 +1,488 @@
+#include "shadergen.h"
+#include "common/assert.h"
+#include "common/log.h"
+#include
+#include
+Log_SetChannel(ShaderGen);
+
+ShaderGen::ShaderGen(HostDisplay::RenderAPI render_api, bool supports_dual_source_blend)
+ : m_render_api(render_api), m_glsl(render_api != HostDisplay::RenderAPI::D3D11),
+ m_supports_dual_source_blend(supports_dual_source_blend), m_use_glsl_interface_blocks(false)
+{
+ if (m_glsl)
+ {
+ if (m_render_api == HostDisplay::RenderAPI::OpenGL || m_render_api == HostDisplay::RenderAPI::OpenGLES)
+ SetGLSLVersionString();
+
+ m_use_glsl_interface_blocks = (IsVulkan() || GLAD_GL_ES_VERSION_3_2 || GLAD_GL_VERSION_3_2);
+ m_use_glsl_binding_layout = (IsVulkan() || UseGLSLBindingLayout());
+ }
+}
+
+ShaderGen::~ShaderGen() = default;
+
+bool ShaderGen::UseGLSLBindingLayout()
+{
+ return (GLAD_GL_ES_VERSION_3_1 || GLAD_GL_VERSION_4_2 ||
+ (GLAD_GL_ARB_explicit_attrib_location && GLAD_GL_ARB_explicit_uniform_location &&
+ GLAD_GL_ARB_shading_language_420pack));
+}
+
+void ShaderGen::DefineMacro(std::stringstream& ss, const char* name, bool enabled)
+{
+ ss << "#define " << name << " " << BoolToUInt32(enabled) << "\n";
+}
+
+void ShaderGen::SetGLSLVersionString()
+{
+ const char* glsl_version = reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION));
+ const bool glsl_es = (m_render_api == HostDisplay::RenderAPI::OpenGLES);
+ Assert(glsl_version != nullptr);
+
+ // Skip any strings in front of the version code.
+ const char* glsl_version_start = glsl_version;
+ while (*glsl_version_start != '\0' && (*glsl_version_start < '0' || *glsl_version_start > '9'))
+ glsl_version_start++;
+
+ int major_version = 0, minor_version = 0;
+ if (std::sscanf(glsl_version_start, "%d.%d", &major_version, &minor_version) == 2)
+ {
+ // Cap at GLSL 4.3, we're not using anything newer for now.
+ if (!glsl_es && (major_version > 4 || (major_version == 4 && minor_version > 30)))
+ {
+ major_version = 4;
+ minor_version = 30;
+ }
+ else if (glsl_es && (major_version > 3 || (major_version == 3 && minor_version > 20)))
+ {
+ major_version = 3;
+ minor_version = 20;
+ }
+ }
+ else
+ {
+ Log_ErrorPrintf("Invalid GLSL version string: '%s' ('%s')", glsl_version, glsl_version_start);
+ if (glsl_es)
+ {
+ major_version = 3;
+ minor_version = 0;
+ }
+ m_glsl_version_string = glsl_es ? "300" : "130";
+ }
+
+ char buf[128];
+ std::snprintf(buf, sizeof(buf), "#version %d%02d%s", major_version, minor_version,
+ (glsl_es && major_version >= 3) ? " es" : "");
+ m_glsl_version_string = buf;
+}
+
+void ShaderGen::WriteHeader(std::stringstream& ss)
+{
+ if (m_render_api == HostDisplay::RenderAPI::OpenGL || m_render_api == HostDisplay::RenderAPI::OpenGLES)
+ ss << m_glsl_version_string << "\n\n";
+ else if (m_render_api == HostDisplay::RenderAPI::Vulkan)
+ ss << "#version 450 core\n\n";
+
+ // Extension enabling for OpenGL.
+ if (m_render_api == HostDisplay::RenderAPI::OpenGLES)
+ {
+ // Enable EXT_blend_func_extended for dual-source blend on OpenGL ES.
+ if (GLAD_GL_EXT_blend_func_extended)
+ ss << "#extension GL_EXT_blend_func_extended : require\n";
+ }
+ else if (m_render_api == HostDisplay::RenderAPI::OpenGL)
+ {
+ // Need extensions for binding layout if GL<4.3.
+ if (m_use_glsl_binding_layout && !GLAD_GL_VERSION_4_3)
+ {
+ ss << "#extension GL_ARB_explicit_attrib_location : require\n";
+ ss << "#extension GL_ARB_explicit_uniform_location : require\n";
+ ss << "#extension GL_ARB_shading_language_420pack : require\n";
+ }
+
+ if (!GLAD_GL_VERSION_3_2)
+ ss << "#extension GL_ARB_uniform_buffer_object : require\n";
+
+ // Enable SSBOs if it's not required by the version.
+ if (!GLAD_GL_VERSION_4_3 && !GLAD_GL_ES_VERSION_3_1 && GLAD_GL_ARB_shader_storage_buffer_object)
+ ss << "#extension GL_ARB_shader_storage_buffer_object : require\n";
+ }
+
+ DefineMacro(ss, "API_OPENGL", m_render_api == HostDisplay::RenderAPI::OpenGL);
+ DefineMacro(ss, "API_OPENGL_ES", m_render_api == HostDisplay::RenderAPI::OpenGLES);
+ DefineMacro(ss, "API_D3D11", m_render_api == HostDisplay::RenderAPI::D3D11);
+ DefineMacro(ss, "API_VULKAN", m_render_api == HostDisplay::RenderAPI::Vulkan);
+
+ if (m_render_api == HostDisplay::RenderAPI::OpenGLES)
+ {
+ ss << "precision highp float;\n";
+ ss << "precision highp int;\n";
+ ss << "precision highp sampler2D;\n";
+
+ if (GLAD_GL_ES_VERSION_3_2)
+ ss << "precision highp usamplerBuffer;\n";
+
+ ss << "\n";
+ }
+
+ if (m_glsl)
+ {
+ ss << "#define GLSL 1\n";
+ ss << "#define float2 vec2\n";
+ ss << "#define float3 vec3\n";
+ ss << "#define float4 vec4\n";
+ ss << "#define int2 ivec2\n";
+ ss << "#define int3 ivec3\n";
+ ss << "#define int4 ivec4\n";
+ ss << "#define uint2 uvec2\n";
+ ss << "#define uint3 uvec3\n";
+ ss << "#define uint4 uvec4\n";
+ ss << "#define nointerpolation flat\n";
+ ss << "#define frac fract\n";
+ ss << "#define lerp mix\n";
+
+ ss << "#define CONSTANT const\n";
+ ss << "#define VECTOR_EQ(a, b) ((a) == (b))\n";
+ ss << "#define VECTOR_NEQ(a, b) ((a) != (b))\n";
+ ss << "#define VECTOR_COMP_EQ(a, b) equal((a), (b))\n";
+ ss << "#define VECTOR_COMP_NEQ(a, b) notEqual((a), (b))\n";
+ ss << "#define SAMPLE_TEXTURE(name, coords) texture(name, coords)\n";
+ ss << "#define LOAD_TEXTURE(name, coords, mip) texelFetch(name, coords, mip)\n";
+ ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) texelFetchOffset(name, coords, mip, offset)\n";
+ ss << "#define LOAD_TEXTURE_BUFFER(name, index) texelFetch(name, index)\n";
+ }
+ else
+ {
+ ss << "#define HLSL 1\n";
+ ss << "#define roundEven round\n";
+ ss << "#define CONSTANT static const\n";
+ ss << "#define VECTOR_EQ(a, b) (all((a) == (b)))\n";
+ ss << "#define VECTOR_NEQ(a, b) (any((a) != (b)))\n";
+ ss << "#define VECTOR_COMP_EQ(a, b) ((a) == (b))\n";
+ ss << "#define VECTOR_COMP_NEQ(a, b) ((a) != (b))\n";
+ ss << "#define SAMPLE_TEXTURE(name, coords) name.Sample(name##_ss, coords)\n";
+ ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
+ ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) name.Load(int3(coords, mip), offset)\n";
+ ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(index)\n";
+ }
+
+ ss << "\n";
+}
+
+void ShaderGen::DeclareUniformBuffer(std::stringstream& ss, const std::initializer_list& members,
+ bool push_constant_on_vulkan)
+{
+ if (IsVulkan())
+ {
+ if (push_constant_on_vulkan)
+ ss << "layout(push_constant) uniform PushConstants\n";
+ else
+ ss << "layout(std140, set = 0, binding = 0) uniform UBOBlock\n";
+ }
+ else if (m_glsl)
+ {
+ if (m_use_glsl_binding_layout)
+ ss << "layout(std140, binding = 1) uniform UBOBlock\n";
+ else
+ ss << "layout(std140) uniform UBOBlock\n";
+ }
+ else
+ {
+ ss << "cbuffer UBOBlock : register(b0)\n";
+ }
+
+ ss << "{\n";
+ for (const char* member : members)
+ ss << member << ";\n";
+ ss << "};\n\n";
+}
+
+void ShaderGen::DeclareTexture(std::stringstream& ss, const char* name, u32 index)
+{
+ if (m_glsl)
+ {
+ if (IsVulkan())
+ ss << "layout(set = 0, binding = " << (index + 1u) << ") ";
+ else if (m_use_glsl_binding_layout)
+ ss << "layout(binding = " << index << ") ";
+
+ ss << "uniform sampler2D " << name << ";\n";
+ }
+ else
+ {
+ ss << "Texture2D " << name << " : register(t" << index << ");\n";
+ ss << "SamplerState " << name << "_ss : register(s" << index << ");\n";
+ }
+}
+
+void ShaderGen::DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned)
+{
+ if (m_glsl)
+ {
+ if (IsVulkan())
+ ss << "layout(set = 0, binding = " << index << ") ";
+ else if (m_use_glsl_binding_layout)
+ ss << "layout(binding = " << index << ") ";
+
+ ss << "uniform " << (is_int ? (is_unsigned ? "u" : "i") : "") << "samplerBuffer " << name << ";\n";
+ }
+ else
+ {
+ ss << "Buffer<" << (is_int ? (is_unsigned ? "uint4" : "int4") : "float4") << "> " << name << " : register(t"
+ << index << ");\n";
+ }
+}
+
+void ShaderGen::DeclareVertexEntryPoint(
+ std::stringstream& ss, const std::initializer_list& attributes, u32 num_color_outputs,
+ u32 num_texcoord_outputs, const std::initializer_list>& additional_outputs,
+ bool declare_vertex_id, const char* output_block_suffix)
+{
+ if (m_glsl)
+ {
+ if (m_use_glsl_binding_layout)
+ {
+ u32 attribute_counter = 0;
+ for (const char* attribute : attributes)
+ {
+ ss << "layout(location = " << attribute_counter << ") in " << attribute << ";\n";
+ attribute_counter++;
+ }
+ }
+ else
+ {
+ for (const char* attribute : attributes)
+ ss << "in " << attribute << ";\n";
+ }
+
+ if (m_use_glsl_interface_blocks)
+ {
+ if (IsVulkan())
+ ss << "layout(location = 0) ";
+
+ ss << "out VertexData" << output_block_suffix << " {\n";
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << " float4 v_col" << i << ";\n";
+
+ for (u32 i = 0; i < num_texcoord_outputs; i++)
+ ss << " float2 v_tex" << i << ";\n";
+
+ for (const auto [qualifiers, name] : additional_outputs)
+ ss << " " << qualifiers << " " << name << ";\n";
+ ss << "};\n";
+ }
+ else
+ {
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << "out float4 v_col" << i << ";\n";
+
+ for (u32 i = 0; i < num_texcoord_outputs; i++)
+ ss << "out float2 v_tex" << i << ";\n";
+
+ for (const auto [qualifiers, name] : additional_outputs)
+ ss << qualifiers << " out " << name << ";\n";
+ }
+
+ ss << "#define v_pos gl_Position\n\n";
+ if (declare_vertex_id)
+ {
+ if (IsVulkan())
+ ss << "#define v_id uint(gl_VertexIndex)\n";
+ else
+ ss << "#define v_id uint(gl_VertexID)\n";
+ }
+
+ ss << "\n";
+ ss << "void main()\n";
+ }
+ else
+ {
+ ss << "void main(\n";
+
+ if (declare_vertex_id)
+ ss << " in uint v_id : SV_VertexID,\n";
+
+ u32 attribute_counter = 0;
+ for (const char* attribute : attributes)
+ {
+ ss << " in " << attribute << " : ATTR" << attribute_counter << ",\n";
+ attribute_counter++;
+ }
+
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << " out float4 v_col" << i << " : COLOR" << i << ",\n";
+
+ for (u32 i = 0; i < num_texcoord_outputs; i++)
+ ss << " out float2 v_tex" << i << " : TEXCOORD" << i << ",\n";
+
+ u32 additional_counter = num_texcoord_outputs;
+ for (const auto [qualifiers, name] : additional_outputs)
+ {
+ ss << " " << qualifiers << " out " << name << " : TEXCOORD" << additional_counter << ",\n";
+ additional_counter++;
+ }
+
+ ss << " out float4 v_pos : SV_Position)\n";
+ }
+}
+
+void ShaderGen::DeclareFragmentEntryPoint(
+ std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
+ const std::initializer_list>& additional_inputs,
+ bool declare_fragcoord /* = false */, u32 num_color_outputs /* = 1 */, bool depth_output /* = false */)
+{
+ if (m_glsl)
+ {
+ if (m_use_glsl_interface_blocks)
+ {
+ if (IsVulkan())
+ ss << "layout(location = 0) ";
+
+ ss << "in VertexData {\n";
+ for (u32 i = 0; i < num_color_inputs; i++)
+ ss << " float4 v_col" << i << ";\n";
+
+ for (u32 i = 0; i < num_texcoord_inputs; i++)
+ ss << " float2 v_tex" << i << ";\n";
+
+ for (const auto [qualifiers, name] : additional_inputs)
+ ss << " " << qualifiers << " " << name << ";\n";
+ ss << "};\n";
+ }
+ else
+ {
+ for (u32 i = 0; i < num_color_inputs; i++)
+ ss << "in float4 v_col" << i << ";\n";
+
+ for (u32 i = 0; i < num_texcoord_inputs; i++)
+ ss << "in float2 v_tex" << i << ";\n";
+
+ for (const auto [qualifiers, name] : additional_inputs)
+ ss << qualifiers << " in " << name << ";\n";
+ }
+
+ if (declare_fragcoord)
+ ss << "#define v_pos gl_FragCoord\n";
+
+ if (depth_output)
+ ss << "#define o_depth gl_FragDepth\n";
+
+ if (m_use_glsl_binding_layout)
+ {
+ if (m_supports_dual_source_blend)
+ {
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << "layout(location = 0, index = " << i << ") out float4 o_col" << i << ";\n";
+ }
+ else
+ {
+ Assert(num_color_outputs <= 1);
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << "layout(location = 0" << i << ") out float4 o_col" << i << ";\n";
+ }
+ }
+ else
+ {
+ for (u32 i = 0; i < num_color_outputs; i++)
+ ss << "out float4 o_col" << i << ";\n";
+ }
+
+ ss << "\n";
+
+ ss << "void main()\n";
+ }
+ else
+ {
+ {
+ ss << "void main(\n";
+
+ for (u32 i = 0; i < num_color_inputs; i++)
+ ss << " in float4 v_col" << i << " : COLOR" << i << ",\n";
+
+ for (u32 i = 0; i < num_texcoord_inputs; i++)
+ ss << " in float2 v_tex" << i << " : TEXCOORD" << i << ",\n";
+
+ u32 additional_counter = num_texcoord_inputs;
+ for (const auto [qualifiers, name] : additional_inputs)
+ {
+ ss << " " << qualifiers << " in " << name << " : TEXCOORD" << additional_counter << ",\n";
+ additional_counter++;
+ }
+
+ if (declare_fragcoord)
+ ss << " in float4 v_pos : SV_Position,\n";
+
+ if (depth_output)
+ {
+ ss << " out float o_depth : SV_Depth";
+ if (num_color_outputs > 0)
+ ss << ",\n";
+ else
+ ss << ")\n";
+ }
+
+ for (u32 i = 0; i < num_color_outputs; i++)
+ {
+ ss << " out float4 o_col" << i << " : SV_Target" << i;
+
+ if (i == (num_color_outputs - 1))
+ ss << ")\n";
+ else
+ ss << ",\n";
+ }
+ }
+ }
+}
+
+std::string ShaderGen::GenerateScreenQuadVertexShader()
+{
+ std::stringstream ss;
+ WriteHeader(ss);
+ DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true);
+ ss << R"(
+{
+ v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));
+ v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);
+ #if API_OPENGL || API_OPENGL_ES || API_VULKAN
+ v_pos.y = -v_pos.y;
+ #endif
+}
+)";
+
+ return ss.str();
+}
+
+std::string ShaderGen::GenerateFillFragmentShader()
+{
+ std::stringstream ss;
+ WriteHeader(ss);
+ DeclareUniformBuffer(ss, {"float4 u_fill_color"}, true);
+ DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1, true);
+
+ ss << R"(
+{
+ o_col0 = u_fill_color;
+ o_depth = u_fill_color.a;
+}
+)";
+
+ return ss.str();
+}
+
+std::string ShaderGen::GenerateCopyFragmentShader()
+{
+ std::stringstream ss;
+ WriteHeader(ss);
+ DeclareUniformBuffer(ss, {"float4 u_src_rect"}, true);
+ DeclareTexture(ss, "samp0", 0);
+ DeclareFragmentEntryPoint(ss, 0, 1, {}, false, 1);
+
+ ss << R"(
+{
+ float2 coords = u_src_rect.xy + v_tex0 * u_src_rect.zw;
+ o_col0 = SAMPLE_TEXTURE(samp0, coords);
+}
+)";
+
+ return ss.str();
+}
diff --git a/src/core/shadergen.h b/src/core/shadergen.h
new file mode 100644
index 000000000..f44742635
--- /dev/null
+++ b/src/core/shadergen.h
@@ -0,0 +1,44 @@
+#pragma once
+#include "gpu_hw.h"
+#include "host_display.h"
+#include
+#include
+
+class ShaderGen
+{
+public:
+ ShaderGen(HostDisplay::RenderAPI render_api, bool supports_dual_source_blend);
+ ~ShaderGen();
+
+ static bool UseGLSLBindingLayout();
+
+ std::string GenerateScreenQuadVertexShader();
+ std::string GenerateFillFragmentShader();
+ std::string GenerateCopyFragmentShader();
+
+protected:
+ ALWAYS_INLINE bool IsVulkan() const { return (m_render_api == HostDisplay::RenderAPI::Vulkan); }
+
+ void SetGLSLVersionString();
+ void DefineMacro(std::stringstream& ss, const char* name, bool enabled);
+ void WriteHeader(std::stringstream& ss);
+ void DeclareUniformBuffer(std::stringstream& ss, const std::initializer_list& members,
+ bool push_constant_on_vulkan);
+ void DeclareTexture(std::stringstream& ss, const char* name, u32 index);
+ void DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned);
+ void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list& attributes,
+ u32 num_color_outputs, u32 num_texcoord_outputs,
+ const std::initializer_list>& additional_outputs,
+ bool declare_vertex_id = false, const char* output_block_suffix = "");
+ void DeclareFragmentEntryPoint(std::stringstream& ss, u32 num_color_inputs, u32 num_texcoord_inputs,
+ const std::initializer_list>& additional_inputs,
+ bool declare_fragcoord = false, u32 num_color_outputs = 1, bool depth_output = false);
+
+ HostDisplay::RenderAPI m_render_api;
+ bool m_glsl;
+ bool m_supports_dual_source_blend;
+ bool m_use_glsl_interface_blocks;
+ bool m_use_glsl_binding_layout;
+
+ std::string m_glsl_version_string;
+};