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; +};