Merge pull request #5264 from lioncash/gen

gl_shader_gen: Make use of fmt where applicable
This commit is contained in:
Ben 2020-05-09 12:28:57 +02:00 committed by GitHub
commit 96ebd53466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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