diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 4879779aa..2601ad58e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -4,8 +4,8 @@ #include #include -#include #include +#include #include "common/assert.h" #include "common/bit_field.h" #include "common/bit_set.h" @@ -82,11 +82,11 @@ layout (std140) uniform shader_data { static std::string GetVertexInterfaceDeclaration(bool is_output, bool separable_shader) { std::string out; - auto append_variable = [&](const char* var, int location) { + const auto append_variable = [&](std::string_view var, int location) { if (separable_shader) { - out += "layout (location=" + std::to_string(location) + ") "; + out += fmt::format("layout (location={}) ", location); } - out += std::string(is_output ? "out " : "in ") + var + ";\n"; + out += fmt::format("{}{};\n", is_output ? "out " : "in ", var); }; append_variable("vec4 primary_color", ATTRIBUTE_COLOR); @@ -267,11 +267,12 @@ void PicaGSConfigCommonRaw::Init(const Pica::Regs& regs) { semantic_maps.fill({16, 0}); for (u32 attrib = 0; attrib < regs.rasterizer.vs_output_total; ++attrib) { - std::array semantics = { - regs.rasterizer.vs_output_attributes[attrib].map_x, - regs.rasterizer.vs_output_attributes[attrib].map_y, - regs.rasterizer.vs_output_attributes[attrib].map_z, - regs.rasterizer.vs_output_attributes[attrib].map_w}; + const std::array semantics{ + regs.rasterizer.vs_output_attributes[attrib].map_x.Value(), + regs.rasterizer.vs_output_attributes[attrib].map_y.Value(), + regs.rasterizer.vs_output_attributes[attrib].map_z.Value(), + regs.rasterizer.vs_output_attributes[attrib].map_w.Value(), + }; for (u32 comp = 0; comp < 4; ++comp) { const auto semantic = semantics[comp]; if (static_cast(semantic) < 24) { @@ -342,7 +343,7 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un /// Writes the specified TEV stage source component(s) static void AppendSource(std::string& out, const PicaFSConfig& config, - TevStageConfig::Source source, const std::string& index_name) { + TevStageConfig::Source source, std::string_view index_name) { using Source = TevStageConfig::Source; switch (source) { case Source::PrimaryColor: @@ -370,7 +371,9 @@ static void AppendSource(std::string& out, const PicaFSConfig& config, out += "combiner_buffer"; break; case Source::Constant: - ((out += "const_color[") += index_name) += ']'; + out += "const_color["; + out += index_name; + out += ']'; break; case Source::Previous: out += "last_tex_env_out"; @@ -385,7 +388,7 @@ static void AppendSource(std::string& out, const PicaFSConfig& config, /// Writes the color components to use for the specified TEV stage color modifier static void AppendColorModifier(std::string& out, const PicaFSConfig& config, TevStageConfig::ColorModifier modifier, - TevStageConfig::Source source, const std::string& index_name) { + TevStageConfig::Source source, std::string_view index_name) { using ColorModifier = TevStageConfig::ColorModifier; switch (modifier) { case ColorModifier::SourceColor: @@ -491,40 +494,38 @@ static void AppendAlphaModifier(std::string& out, const PicaFSConfig& config, /// Writes the combiner function for the color components for the specified TEV stage operation static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, - const std::string& variable_name) { + std::string_view variable_name) { out += "clamp("; using Operation = TevStageConfig::Operation; switch (operation) { case Operation::Replace: - out += variable_name + "[0]"; + out += fmt::format("{}[0]", variable_name); break; case Operation::Modulate: - out += variable_name + "[0] * " + variable_name + "[1]"; + out += fmt::format("{0}[0] * {0}[1]", variable_name); break; case Operation::Add: - out += variable_name + "[0] + " + variable_name + "[1]"; + out += fmt::format("{0}[0] + {0}[1]", variable_name); break; case Operation::AddSigned: - out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)"; + out += fmt::format("{0}[0] + {0}[1] - vec3(0.5)", variable_name); break; case Operation::Lerp: - out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + - "[1] * (vec3(1.0) - " + variable_name + "[2])"; + out += fmt::format("{0}[0] * {0}[2] + {0}[1] * (vec3(1.0) - {0}[2])", variable_name); break; case Operation::Subtract: - out += variable_name + "[0] - " + variable_name + "[1]"; + out += fmt::format("{0}[0] - {0}[1]", variable_name); break; case Operation::MultiplyThenAdd: - out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; + out += fmt::format("{0}[0] * {0}[1] + {0}[2]", variable_name); break; case Operation::AddThenMultiply: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + - variable_name + "[2]"; + out += fmt::format("min({0}[0] + {0}[1], vec3(1.0)) * {0}[2]", variable_name); break; case Operation::Dot3_RGB: case Operation::Dot3_RGBA: - out += "vec3(dot(" + variable_name + "[0] - vec3(0.5), " + variable_name + - "[1] - vec3(0.5)) * 4.0)"; + out += + fmt::format("vec3(dot({0}[0] - vec3(0.5), {0}[1] - vec3(0.5)) * 4.0)", variable_name); break; default: out += "vec3(0.0)"; @@ -537,35 +538,33 @@ static void AppendColorCombiner(std::string& out, TevStageConfig::Operation oper /// Writes the combiner function for the alpha component for the specified TEV stage operation static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, - const std::string& variable_name) { + std::string_view variable_name) { out += "clamp("; using Operation = TevStageConfig::Operation; switch (operation) { case Operation::Replace: - out += variable_name + "[0]"; + out += fmt::format("{}[0]", variable_name); break; case Operation::Modulate: - out += variable_name + "[0] * " + variable_name + "[1]"; + out += fmt::format("{0}[0] * {0}[1]", variable_name); break; case Operation::Add: - out += variable_name + "[0] + " + variable_name + "[1]"; + out += fmt::format("{0}[0] + {0}[1]", variable_name); break; case Operation::AddSigned: - out += variable_name + "[0] + " + variable_name + "[1] - 0.5"; + out += fmt::format("{0}[0] + {0}[1] - 0.5", variable_name); break; case Operation::Lerp: - out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + - "[1] * (1.0 - " + variable_name + "[2])"; + out += fmt::format("{0}[0] * {0}[2] + {0}[1] * (1.0 - {0}[2])", variable_name); break; case Operation::Subtract: - out += variable_name + "[0] - " + variable_name + "[1]"; + out += fmt::format("{0}[0] - {0}[1]", variable_name); break; case Operation::MultiplyThenAdd: - out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; + out += fmt::format("{0}[0] * {0}[1] + {0}[2]", variable_name); break; case Operation::AddThenMultiply: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + - "[2]"; + out += fmt::format("min({0}[0] + {0}[1], 1.0) * {0}[2]", variable_name); break; default: out += "0.0"; @@ -592,9 +591,9 @@ static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareF case CompareFunc::LessThanOrEqual: case CompareFunc::GreaterThan: case CompareFunc::GreaterThanOrEqual: { - static const char* op[] = {"!=", "==", ">=", ">", "<=", "<"}; - unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; - out += "int(last_tex_env_out.a * 255.0) " + std::string(op[index]) + " alphatest_ref"; + static constexpr std::array op{"!=", "==", ">=", ">", "<=", "<"}; + const auto index = static_cast(func) - static_cast(CompareFunc::Equal); + out += fmt::format("int(last_tex_env_out.a * 255.0) {} alphatest_ref", op[index]); break; } @@ -610,9 +609,9 @@ static void WriteTevStage(std::string& out, const PicaFSConfig& config, unsigned const auto stage = static_cast(config.state.tev_stages[index]); if (!IsPassThroughTevStage(stage)) { - std::string index_name = std::to_string(index); + const std::string index_name = std::to_string(index); - out += "vec3 color_results_" + index_name + "[3] = vec3[3]("; + out += fmt::format("vec3 color_results_{}[3] = vec3[3](", index_name); AppendColorModifier(out, config, stage.color_modifier1, stage.color_source1, index_name); out += ", "; AppendColorModifier(out, config, stage.color_modifier2, stage.color_source2, index_name); @@ -621,15 +620,15 @@ static void WriteTevStage(std::string& out, const PicaFSConfig& config, unsigned out += ");\n"; // Round the output of each TEV stage to maintain the PICA's 8 bits of precision - out += "vec3 color_output_" + index_name + " = byteround("; + out += fmt::format("vec3 color_output_{} = byteround(", index_name); AppendColorCombiner(out, stage.color_op, "color_results_" + index_name); out += ");\n"; if (stage.color_op == TevStageConfig::Operation::Dot3_RGBA) { // result of Dot3_RGBA operation is also placed to the alpha component - out += "float alpha_output_" + index_name + " = color_output_" + index_name + "[0];\n"; + out += fmt::format("float alpha_output_{0} = color_output_{0}[0];\n", index_name); } else { - out += "float alpha_results_" + index_name + "[3] = float[3]("; + out += fmt::format("float alpha_results_{}[3] = float[3](", index_name); AppendAlphaModifier(out, config, stage.alpha_modifier1, stage.alpha_source1, index_name); out += ", "; @@ -640,18 +639,16 @@ static void WriteTevStage(std::string& out, const PicaFSConfig& config, unsigned index_name); out += ");\n"; - out += "float alpha_output_" + index_name + " = byteround("; + out += fmt::format("float alpha_output_{} = byteround(", index_name); AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); out += ");\n"; } - out += "last_tex_env_out = vec4(" - "clamp(color_output_" + - index_name + " * " + std::to_string(stage.GetColorMultiplier()) + - ".0, vec3(0.0), vec3(1.0))," - "clamp(alpha_output_" + - index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + - ".0, 0.0, 1.0));\n"; + out += fmt::format("last_tex_env_out = vec4(" + "clamp(color_output_{} * {}.0, vec3(0.0), vec3(1.0)), " + "clamp(alpha_output_{} * {}.0, 0.0, 1.0));\n", + index_name, stage.GetColorMultiplier(), index_name, + stage.GetAlphaMultiplier()); } out += "combiner_buffer = next_combiner_buffer;\n"; @@ -679,26 +676,26 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { "float geo_factor = 1.0;\n"; // Compute fragment normals and tangents - auto Perturbation = [&]() { - return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; + const auto Perturbation = [&] { + return fmt::format("2.0 * ({}).rgb - 1.0", SampleTexture(config, lighting.bump_selector)); }; if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { // Bump mapping is enabled using a normal map - out += "vec3 surface_normal = " + Perturbation() + ";\n"; + out += fmt::format("vec3 surface_normal = {};\n", Perturbation()); // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher // precision result if (lighting.bump_renorm) { - std::string val = + constexpr std::string_view val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; - out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; + out += fmt::format("surface_normal.z = sqrt(max({}, 0.0));\n", val); } // The tangent vector is not perturbed by the normal map and is just a unit vector. out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { // Bump mapping is enabled using a tangent map - out += "vec3 surface_tangent = " + Perturbation() + ";\n"; + out += fmt::format("vec3 surface_tangent = {};\n", Perturbation()); // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant // computation below, which is also confirmed on 3DS. So we don't bother recomputing here // even if 'renorm' is enabled. @@ -707,22 +704,22 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; } else { // No bump mapping - surface local normal and tangent are just unit vectors - out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; - out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; + out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n" + "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; } // Rotate the surface-local normal by the interpolated normal quaternion to convert it to // eyespace. - out += "vec4 normalized_normquat = normalize(normquat);\n"; - out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; - out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; + out += "vec4 normalized_normquat = normalize(normquat);\n" + "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n" + "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; if (lighting.enable_shadow) { std::string shadow_texture = SampleTexture(config, lighting.shadow_selector); if (lighting.shadow_invert) { - out += "vec4 shadow = vec4(1.0) - " + shadow_texture + ";\n"; + out += fmt::format("vec4 shadow = vec4(1.0) - {};\n", shadow_texture); } else { - out += "vec4 shadow = " + shadow_texture + ";\n"; + out += fmt::format("vec4 shadow = {};\n", shadow_texture); } } else { out += "vec4 shadow = vec4(1.0);\n"; @@ -738,19 +735,19 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { break; case LightingRegs::LightingLutInput::VH: - index = std::string("dot(normalize(view), normalize(half_vector))"); + index = "dot(normalize(view), normalize(half_vector))"; break; case LightingRegs::LightingLutInput::NV: - index = std::string("dot(normal, normalize(view))"); + index = "dot(normal, normalize(view))"; break; case LightingRegs::LightingLutInput::LN: - index = std::string("dot(light_vector, normal)"); + index = "dot(light_vector, normal)"; break; case LightingRegs::LightingLutInput::SP: - index = std::string("dot(light_vector, spot_dir)"); + index = "dot(light_vector, spot_dir)"; break; case LightingRegs::LightingLutInput::CP: @@ -759,11 +756,11 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { // Note: even if the normal vector is modified by normal map, which is not the // normal of the tangent plane anymore, the half angle vector is still projected // using the modified normal vector. - std::string half_angle_proj = + constexpr std::string_view half_angle_proj = "normalize(half_vector) - normal * dot(normal, normalize(half_vector))"; // Note: the half angle vector projection is confirmed not normalized before the dot // product. The result is in fact not cos(phi) as the name suggested. - index = "dot(" + half_angle_proj + ", tangent)"; + index = fmt::format("dot({}, tangent)", half_angle_proj); } else { index = "0.0"; } @@ -776,31 +773,33 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { break; } - std::string sampler_string = std::to_string(static_cast(sampler)); + const auto sampler_index = static_cast(sampler); if (abs) { // LUT index is in the range of (0.0, 1.0) - index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" - : "max(" + index + ", 0.0)"; - return "LookupLightingLUTUnsigned(" + sampler_string + ", " + index + ")"; + index = lighting.light[light_num].two_sided_diffuse + ? fmt::format("abs({})", index) + : fmt::format("max({}, 0.0)", index); + return fmt::format("LookupLightingLUTUnsigned({}, {})", sampler_index, index); } else { // LUT index is in the range of (-1.0, 1.0) - return "LookupLightingLUTSigned(" + sampler_string + ", " + index + ")"; + return fmt::format("LookupLightingLUTSigned({}, {})", sampler_index, index); } }; // Write the code to emulate each enabled light for (unsigned light_index = 0; light_index < lighting.src_num; ++light_index) { const auto& light_config = lighting.light[light_index]; - std::string light_src = "light_src[" + std::to_string(light_config.num) + "]"; + const std::string light_src = fmt::format("light_src[{}]", light_config.num); // Compute light vector (directional or positional) - if (light_config.directional) - out += "light_vector = normalize(" + light_src + ".position);\n"; - else - out += "light_vector = normalize(" + light_src + ".position + view);\n"; + if (light_config.directional) { + out += fmt::format("light_vector = normalize({}.position);\n", light_src); + } else { + out += fmt::format("light_vector = normalize({}.position + view);\n", light_src); + } - out += "spot_dir = " + light_src + ".spot_direction;\n"; + out += fmt::format("spot_dir = {}.spot_direction;\n", light_src); out += "half_vector = normalize(view) + light_vector;\n"; // Compute dot product of light_vector and normal, adjust if lighting is one-sided or @@ -819,21 +818,21 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { if (light_config.spot_atten_enable && LightingRegs::IsLightingSamplerSupported( lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { - std::string value = + const std::string value = GetLutValue(LightingRegs::SpotlightAttenuationSampler(light_config.num), light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); - spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + value + ")"; + spot_atten = fmt::format("({} * {})", lighting.lut_sp.scale, value); } // If enabled, compute distance attenuation value std::string dist_atten = "1.0"; if (light_config.dist_atten_enable) { - std::string index = "clamp(" + light_src + ".dist_atten_scale * length(-view - " + - light_src + ".position) + " + light_src + - ".dist_atten_bias, 0.0, 1.0)"; - auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); - dist_atten = "LookupLightingLUTUnsigned(" + - std::to_string(static_cast(sampler)) + "," + index + ")"; + const std::string index = fmt::format("clamp({}.dist_atten_scale * length(-view - " + "{}.position) + {}.dist_atten_bias, 0.0, 1.0)", + light_src, light_src, light_src); + const auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); + dist_atten = + fmt::format("LookupLightingLUTUnsigned({}, {})", static_cast(sampler), index); } if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { @@ -848,14 +847,14 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { LightingRegs::IsLightingSamplerSupported( lighting.config, LightingRegs::LightingSampler::Distribution0)) { // Lookup specular "distribution 0" LUT value - std::string value = + const std::string value = GetLutValue(LightingRegs::LightingSampler::Distribution0, light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input); - d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + value + ")"; + d0_lut_value = fmt::format("({} * {})", lighting.lut_d0.scale, value); } - std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; + std::string specular_0 = fmt::format("({} * {}.specular_0)", d0_lut_value, light_src); if (light_config.geometric_factor_0) { - specular_0 = "(" + specular_0 + " * geo_factor)"; + specular_0 = fmt::format("({} * geo_factor)", specular_0); } // If enabled, lookup ReflectRed value, otherwise, 1.0 is used @@ -865,8 +864,8 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { std::string value = GetLutValue(LightingRegs::LightingSampler::ReflectRed, light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input); - value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + value + ")"; - out += "refl_value.r = " + value + ";\n"; + value = fmt::format("({} * {})", lighting.lut_rr.scale, value); + out += fmt::format("refl_value.r = {};\n", value); } else { out += "refl_value.r = 1.0;\n"; } @@ -878,8 +877,8 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { std::string value = GetLutValue(LightingRegs::LightingSampler::ReflectGreen, light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input); - value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + value + ")"; - out += "refl_value.g = " + value + ";\n"; + value = fmt::format("({} * {})", lighting.lut_rg.scale, value); + out += fmt::format("refl_value.g = {};\n", value); } else { out += "refl_value.g = refl_value.r;\n"; } @@ -891,8 +890,8 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { std::string value = GetLutValue(LightingRegs::LightingSampler::ReflectBlue, light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input); - value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + value + ")"; - out += "refl_value.b = " + value + ";\n"; + value = fmt::format("({} * {})", lighting.lut_rb.scale, value); + out += fmt::format("refl_value.b = {};\n", value); } else { out += "refl_value.b = refl_value.r;\n"; } @@ -903,15 +902,15 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { LightingRegs::IsLightingSamplerSupported( lighting.config, LightingRegs::LightingSampler::Distribution1)) { // Lookup specular "distribution 1" LUT value - std::string value = + const std::string value = GetLutValue(LightingRegs::LightingSampler::Distribution1, light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input); - d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + value + ")"; + d1_lut_value = fmt::format("({} * {})", lighting.lut_d1.scale, value); } std::string specular_1 = - "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; + fmt::format("({} * refl_value * {}.specular_1)", d1_lut_value, light_src); if (light_config.geometric_factor_1) { - specular_1 = "(" + specular_1 + " * geo_factor)"; + specular_1 = fmt::format("({} * geo_factor)", specular_1); } // Fresnel @@ -923,16 +922,16 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { std::string value = GetLutValue(LightingRegs::LightingSampler::Fresnel, light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input); - value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + value + ")"; + value = fmt::format("({} * {})", lighting.lut_fr.scale, value); // Enabled for diffuse lighting alpha component if (lighting.enable_primary_alpha) { - out += "diffuse_sum.a = " + value + ";\n"; + out += fmt::format("diffuse_sum.a = {};\n", value); } // Enabled for the specular lighting alpha component if (lighting.enable_secondary_alpha) { - out += "specular_sum.a = " + value + ";\n"; + out += fmt::format("specular_sum.a = {};\n", value); } } @@ -942,13 +941,13 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { std::string shadow_secondary = shadow_secondary_enable ? " * shadow.rgb" : ""; // Compute primary fragment color (diffuse lighting) function - out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * dot_product) + " + light_src + - ".ambient) * " + dist_atten + " * " + spot_atten + shadow_primary + ";\n"; + out += fmt::format( + "diffuse_sum.rgb += (({}.diffuse * dot_product) + {}.ambient) * {} * {}{};\n", + light_src, light_src, dist_atten, spot_atten, shadow_primary); // Compute secondary fragment color (specular lighting) function - out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + - ") * clamp_highlights * " + dist_atten + " * " + spot_atten + shadow_secondary + - ";\n"; + out += fmt::format("specular_sum.rgb += ({} + {}) * clamp_highlights * {} * {}{};\n", + specular_0, specular_1, dist_atten, spot_atten, shadow_secondary); } // Apply shadow attenuation to alpha components if enabled @@ -962,9 +961,9 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) { } // Sum final lighting result - out += "diffuse_sum.rgb += lighting_global_ambient;\n"; - out += "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n"; - out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n"; + out += "diffuse_sum.rgb += lighting_global_ambient;\n" + "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n" + "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n"; } using ProcTexClamp = TexturingRegs::ProcTexClamp; @@ -972,18 +971,18 @@ using ProcTexShift = TexturingRegs::ProcTexShift; using ProcTexCombiner = TexturingRegs::ProcTexCombiner; using ProcTexFilter = TexturingRegs::ProcTexFilter; -void AppendProcTexShiftOffset(std::string& out, const std::string& v, ProcTexShift mode, +void AppendProcTexShiftOffset(std::string& out, std::string_view v, ProcTexShift mode, ProcTexClamp clamp_mode) { - std::string offset = (clamp_mode == ProcTexClamp::MirroredRepeat) ? "1.0" : "0.5"; + const std::string_view offset = (clamp_mode == ProcTexClamp::MirroredRepeat) ? "1.0" : "0.5"; switch (mode) { case ProcTexShift::None: out += "0.0"; break; case ProcTexShift::Odd: - out += offset + " * float((int(" + v + ") / 2) % 2)"; + out += fmt::format("{} * float((int({}) / 2) % 2)", offset, v); break; case ProcTexShift::Even: - out += offset + " * float(((int(" + v + ") + 1) / 2) % 2)"; + out += fmt::format("{} * float(((int({}) + 1) / 2) % 2)", offset, v); break; default: LOG_CRITICAL(HW_GPU, "Unknown shift mode {}", static_cast(mode)); @@ -992,34 +991,33 @@ void AppendProcTexShiftOffset(std::string& out, const std::string& v, ProcTexShi } } -void AppendProcTexClamp(std::string& out, const std::string& var, ProcTexClamp mode) { +void AppendProcTexClamp(std::string& out, std::string_view var, ProcTexClamp mode) { switch (mode) { case ProcTexClamp::ToZero: - out += var + " = " + var + " > 1.0 ? 0 : " + var + ";\n"; + out += fmt::format("{0} = {0} > 1.0 ? 0 : {0};\n", var); break; case ProcTexClamp::ToEdge: - out += var + " = " + "min(" + var + ", 1.0);\n"; + out += fmt::format("{0} = min({0}, 1.0);\n", var); break; case ProcTexClamp::SymmetricalRepeat: - out += var + " = " + "fract(" + var + ");\n"; + out += fmt::format("{0} = fract({0});\n", var); break; case ProcTexClamp::MirroredRepeat: { - out += - var + " = int(" + var + ") % 2 == 0 ? fract(" + var + ") : 1.0 - fract(" + var + ");\n"; + out += fmt::format("{0} = int({0}) % 2 == 0 ? fract({0}) : 1.0 - fract({0});\n", var); break; } case ProcTexClamp::Pulse: - out += var + " = " + var + " > 0.5 ? 1.0 : 0.0;\n"; + out += fmt::format("{0} = {0} > 0.5 ? 1.0 : 0.0;\n", var); break; default: LOG_CRITICAL(HW_GPU, "Unknown clamp mode {}", static_cast(mode)); - out += var + " = " + "min(" + var + ", 1.0);\n"; + out += fmt::format("{0} = min({0}, 1.0);\n", var); break; } } void AppendProcTexCombineAndMap(std::string& out, ProcTexCombiner combiner, - const std::string& offset) { + std::string_view offset) { std::string combined; switch (combiner) { case ProcTexCombiner::U: @@ -1057,7 +1055,7 @@ void AppendProcTexCombineAndMap(std::string& out, ProcTexCombiner combiner, combined = "0.0"; break; } - out += "ProcTexLookupLUT(" + offset + ", " + combined + ")"; + out += fmt::format("ProcTexLookupLUT({}, {})", offset, combined); } void AppendProcTexSampler(std::string& out, const PicaFSConfig& config) { @@ -1117,14 +1115,11 @@ float ProcTexNoiseCoef(vec2 x) { } out += "vec4 SampleProcTexColor(float lut_coord, int level) {\n"; - out += "int lut_width = " + std::to_string(config.state.proctex.lut_width) + " >> level;\n"; - std::string offset0 = std::to_string(config.state.proctex.lut_offset0); - std::string offset1 = std::to_string(config.state.proctex.lut_offset1); - std::string offset2 = std::to_string(config.state.proctex.lut_offset2); - std::string offset3 = std::to_string(config.state.proctex.lut_offset3); + out += fmt::format("int lut_width = {} >> level;\n", config.state.proctex.lut_width); // Offsets for level 4-7 seem to be hardcoded - out += "int lut_offsets[8] = int[](" + offset0 + ", " + offset1 + ", " + offset2 + ", " + - offset3 + ", 0xF0, 0xF8, 0xFC, 0xFE);\n"; + out += fmt::format("int lut_offsets[8] = int[]({}, {}, {}, {}, 0xF0, 0xF8, 0xFC, 0xFE);\n", + config.state.proctex.lut_offset0, config.state.proctex.lut_offset1, + config.state.proctex.lut_offset2, config.state.proctex.lut_offset3); out += "int lut_offset = lut_offsets[level];\n"; // For the color lut, coord=0.0 is lut[offset] and coord=1.0 is lut[offset+width-1] out += "lut_coord *= float(lut_width - 1);\n"; @@ -1153,7 +1148,7 @@ float ProcTexNoiseCoef(vec2 x) { out += "vec4 ProcTex() {\n"; if (config.state.proctex.coord < 3) { - out += "vec2 uv = abs(texcoord" + std::to_string(config.state.proctex.coord) + ");\n"; + out += fmt::format("vec2 uv = abs(texcoord{});\n", config.state.proctex.coord); } else { LOG_CRITICAL(Render_OpenGL, "Unexpected proctex.coord >= 3"); out += "vec2 uv = abs(texcoord0);\n"; @@ -1165,12 +1160,12 @@ float ProcTexNoiseCoef(vec2 x) { // Note: this is different from the one normal 2D textures use. out += "vec2 duv = max(abs(dFdx(uv)), abs(dFdy(uv)));\n"; // unlike normal texture, the bias is inside the log2 - out += "float lod = log2(abs(float(" + std::to_string(config.state.proctex.lut_width) + - ") * proctex_bias) * (duv.x + duv.y));\n"; + out += fmt::format("float lod = log2(abs(float({}) * proctex_bias) * (duv.x + duv.y));\n", + config.state.proctex.lut_width); out += "if (proctex_bias == 0.0) lod = 0.0;\n"; - out += "lod = clamp(lod, " + - std::to_string(std::max(0.0f, config.state.proctex.lod_min)) + ", " + - std::to_string(std::min(7.0f, config.state.proctex.lod_max)) + ");\n"; + out += fmt::format("lod = clamp(lod, {}, {});\n", + std::max(0.0f, static_cast(config.state.proctex.lod_min)), + std::min(7.0f, static_cast(config.state.proctex.lod_max))); // Get shift offset before noise generation out += "float u_shift = "; AppendProcTexShiftOffset(out, "uv.y", config.state.proctex.u_shift, @@ -1183,13 +1178,13 @@ float ProcTexNoiseCoef(vec2 x) { // Generate noise if (config.state.proctex.noise_enable) { - out += "uv += proctex_noise_a * ProcTexNoiseCoef(uv);\n"; - out += "uv = abs(uv);\n"; + out += "uv += proctex_noise_a * ProcTexNoiseCoef(uv);\n" + "uv = abs(uv);\n"; } // Shift - out += "float u = uv.x + u_shift;\n"; - out += "float v = uv.y + v_shift;\n"; + out += "float u = uv.x + u_shift;\n" + "float v = uv.y + v_shift;\n"; // Clamp AppendProcTexClamp(out, "u", config.state.proctex.u_clamp); @@ -1212,9 +1207,9 @@ float ProcTexNoiseCoef(vec2 x) { break; case ProcTexFilter::NearestMipmapLinear: case ProcTexFilter::LinearMipmapLinear: - out += "int lod_i = int(lod);\n"; - out += "float lod_f = fract(lod);\n"; - out += "vec4 final_color = mix(SampleProcTexColor(lut_coord, lod_i), " + out += "int lod_i = int(lod);\n" + "float lod_f = fract(lod);\n" + "vec4 final_color = mix(SampleProcTexColor(lut_coord, lod_i), " "SampleProcTexColor(lut_coord, lod_i + 1), lod_f);\n"; break; } @@ -1491,8 +1486,9 @@ vec4 secondary_fragment_color = vec4(0.0); if (state.scissor_test_mode != RasterizerRegs::ScissorMode::Disabled) { out += "if ("; // Negate the condition if we have to keep only the pixels outside the scissor box - if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include) - out += "!"; + if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include) { + out += '!'; + } out += "(gl_FragCoord.x >= float(scissor_x1) && " "gl_FragCoord.y >= float(scissor_y1) && " "gl_FragCoord.x < float(scissor_x2) && " @@ -1502,8 +1498,8 @@ vec4 secondary_fragment_color = vec4(0.0); // After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use // default near = 0 and far = 1, and undo the transformation to get the original z_over_w, then // do our own transformation according to PICA specification. - out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n"; - out += "float depth = z_over_w * depth_scale + depth_offset;\n"; + out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n" + "float depth = z_over_w * depth_scale + depth_offset;\n"; if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) { out += "depth /= gl_FragCoord.w;\n"; } @@ -1511,12 +1507,13 @@ vec4 secondary_fragment_color = vec4(0.0); if (state.lighting.enable) WriteLighting(out, config); - out += "vec4 combiner_buffer = vec4(0.0);\n"; - out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n"; - out += "vec4 last_tex_env_out = vec4(0.0);\n"; + out += "vec4 combiner_buffer = vec4(0.0);\n" + "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n" + "vec4 last_tex_env_out = vec4(0.0);\n"; - for (std::size_t index = 0; index < state.tev_stages.size(); ++index) - WriteTevStage(out, config, (unsigned)index); + for (std::size_t index = 0; index < state.tev_stages.size(); ++index) { + WriteTevStage(out, config, static_cast(index)); + } if (state.alpha_test_func != FramebufferRegs::CompareFunc::Always) { out += "if ("; @@ -1534,12 +1531,12 @@ vec4 secondary_fragment_color = vec4(0.0); } // Generate clamped fog factor from LUT for given fog index - out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; - out += "float fog_f = fog_index - fog_i;\n"; - out += "vec2 fog_lut_entry = texelFetch(texture_buffer_lut_rg, int(fog_i) + " - "fog_lut_offset).rg;\n"; - out += "float fog_factor = fog_lut_entry.r + fog_lut_entry.g * fog_f;\n"; - out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; + out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n" + "float fog_f = fog_index - fog_i;\n" + "vec2 fog_lut_entry = texelFetch(texture_buffer_lut_rg, int(fog_i) + " + "fog_lut_offset).rg;\n" + "float fog_factor = fog_lut_entry.r + fog_lut_entry.g * fog_f;\n" + "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; // Blend the fog out += "last_tex_env_out.rgb = mix(fog_color.rgb, last_tex_env_out.rgb, fog_factor);\n"; @@ -1584,31 +1581,33 @@ do { out += "color = byteround(last_tex_env_out);\n"; } - out += "}"; + out += '}'; return {out}; } ShaderDecompiler::ProgramResult GenerateTrivialVertexShader(bool separable_shader) { - std::string out = ""; + std::string out; if (separable_shader) { out += "#extension GL_ARB_separate_shader_objects : enable\n"; } - out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + - ") in vec4 vert_position;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + - ") in vec2 vert_texcoord0;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + - ") in vec2 vert_texcoord1;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + - ") in vec2 vert_texcoord2;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0_W) + - ") in float vert_texcoord0_w;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + - ") in vec4 vert_normquat;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n"; + out += fmt::format("layout(location = {}) in vec4 vert_position;\n", + static_cast(ATTRIBUTE_POSITION)); + out += fmt::format("layout(location = {}) in vec4 vert_color;\n", + static_cast(ATTRIBUTE_COLOR)); + out += fmt::format("layout(location = {}) in vec2 vert_texcoord0;\n", + static_cast(ATTRIBUTE_TEXCOORD0)); + out += fmt::format("layout(location = {}) in vec2 vert_texcoord1;\n", + static_cast(ATTRIBUTE_TEXCOORD1)); + out += fmt::format("layout(location = {}) in vec2 vert_texcoord2;\n", + static_cast(ATTRIBUTE_TEXCOORD2)); + out += fmt::format("layout(location = {}) in float vert_texcoord0_w;\n", + static_cast(ATTRIBUTE_TEXCOORD0_W)); + out += fmt::format("layout(location = {}) in vec4 vert_normquat;\n", + static_cast(ATTRIBUTE_NORMQUAT)); + out += + fmt::format("layout(location = {}) in vec3 vert_view;\n", static_cast(ATTRIBUTE_VIEW)); out += GetVertexInterfaceDeclaration(true, separable_shader); @@ -1645,16 +1644,16 @@ std::optional GenerateVertexShader( out += ShaderDecompiler::GetCommonDeclarations(); std::array used_regs{}; - auto get_input_reg = [&](u32 reg) -> std::string { + const auto get_input_reg = [&used_regs](u32 reg) { ASSERT(reg < 16); used_regs[reg] = true; - return "vs_in_reg" + std::to_string(reg); + return fmt::format("vs_in_reg{}", reg); }; - auto get_output_reg = [&](u32 reg) -> std::string { + const auto get_output_reg = [&](u32 reg) -> std::string { ASSERT(reg < 16); if (config.state.output_map[reg] < config.state.num_outputs) { - return "vs_out_attr" + std::to_string(config.state.output_map[reg]); + return fmt::format("vs_out_attr{}", config.state.output_map[reg]); } return ""; }; @@ -1678,11 +1677,10 @@ layout (std140) uniform vs_config { // input attributes declaration for (std::size_t i = 0; i < used_regs.size(); ++i) { if (used_regs[i]) { - out += "layout(location = " + std::to_string(i) + ") in vec4 vs_in_reg" + - std::to_string(i) + ";\n"; + out += fmt::format("layout(location = {0}) in vec4 vs_in_reg{0};\n", i); } } - out += "\n"; + out += '\n'; // output attributes declaration for (u32 i = 0; i < config.state.num_outputs; ++i) { @@ -1692,7 +1690,7 @@ layout (std140) uniform vs_config { out += "\nvoid main() {\n"; for (u32 i = 0; i < config.state.num_outputs; ++i) { - out += " vs_out_attr" + std::to_string(i) + " = vec4(0.0, 0.0, 0.0, 1.0);\n"; + out += fmt::format(" vs_out_attr{} = vec4(0.0, 0.0, 0.0, 1.0);\n", i); } out += "\n exec_shader();\n}\n\n"; @@ -1715,15 +1713,15 @@ static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config, bool s out += R"( struct Vertex { )"; - out += " vec4 attributes[" + std::to_string(config.gs_output_attributes) + "];\n"; + out += fmt::format(" vec4 attributes[{}];\n", config.gs_output_attributes); out += "};\n\n"; - auto semantic = [&config](VSOutputAttributes::Semantic slot_semantic) -> std::string { - u32 slot = static_cast(slot_semantic); - u32 attrib = config.semantic_maps[slot].attribute_index; - u32 comp = config.semantic_maps[slot].component_index; + const auto semantic = [&config](VSOutputAttributes::Semantic slot_semantic) -> std::string { + const u32 slot = static_cast(slot_semantic); + const u32 attrib = config.semantic_maps[slot].attribute_index; + const u32 comp = config.semantic_maps[slot].component_index; if (attrib < config.gs_output_attributes) { - return "vtx.attributes[" + std::to_string(attrib) + "]." + "xyzw"[comp]; + return fmt::format("vtx.attributes[{}].{}", attrib, "xyzw"[comp]); } return "0.0"; }; @@ -1806,11 +1804,10 @@ void main() { Vertex prim_buffer[3]; )"; for (u32 vtx = 0; vtx < 3; ++vtx) { - out += " prim_buffer[" + std::to_string(vtx) + "].attributes = vec4[" + - std::to_string(config.state.gs_output_attributes) + "]("; + out += fmt::format(" prim_buffer[{}].attributes = vec4[{}](", vtx, + config.state.gs_output_attributes); for (u32 i = 0; i < config.state.vs_output_attributes; ++i) { - out += std::string(i == 0 ? "" : ", ") + "vs_out_attr" + std::to_string(i) + "[" + - std::to_string(vtx) + "]"; + out += fmt::format("{}vs_out_attr{}[{}]", i == 0 ? "" : ", ", i, vtx); } out += ");\n"; }