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