gl_shader_gen: Make use of fmt where applicable

Avoids string churn and makes it significantly easier to read the shader
template strings that are being used as the basis for formatting.
This commit is contained in:
Lioncash 2020-04-21 00:30:50 -04:00
parent 36809b2e2e
commit cd201cd60f

View file

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