mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-27 08:15:33 +01:00
Fifo analyzer: Display equations for color/alpha combiners
This commit is contained in:
parent
0afe318b55
commit
95e0f833f9
@ -55,9 +55,9 @@ public:
|
|||||||
constexpr auto parse(fmt::format_parse_context& ctx)
|
constexpr auto parse(fmt::format_parse_context& ctx)
|
||||||
{
|
{
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
// 'u' for user display, 's' for shader generation
|
// 'u' for user display, 's' for shader generation, 'n' for name only
|
||||||
if (it != end && (*it == 'u' || *it == 's'))
|
if (it != end && (*it == 'u' || *it == 's' || *it == 'n'))
|
||||||
formatting_for_shader = (*it++ == 's');
|
format_type = *it++;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,19 +68,24 @@ public:
|
|||||||
const auto value_u = static_cast<std::make_unsigned_t<T>>(value_s); // Always unsigned
|
const auto value_u = static_cast<std::make_unsigned_t<T>>(value_s); // Always unsigned
|
||||||
const bool has_name = m_names.InBounds(e) && m_names[e] != nullptr;
|
const bool has_name = m_names.InBounds(e) && m_names[e] != nullptr;
|
||||||
|
|
||||||
if (!formatting_for_shader)
|
switch (format_type)
|
||||||
{
|
{
|
||||||
|
default:
|
||||||
|
case 'u':
|
||||||
if (has_name)
|
if (has_name)
|
||||||
return fmt::format_to(ctx.out(), "{} ({})", m_names[e], value_s);
|
return fmt::format_to(ctx.out(), "{} ({})", m_names[e], value_s);
|
||||||
else
|
else
|
||||||
return fmt::format_to(ctx.out(), "Invalid ({})", value_s);
|
return fmt::format_to(ctx.out(), "Invalid ({})", value_s);
|
||||||
}
|
case 's':
|
||||||
else
|
|
||||||
{
|
|
||||||
if (has_name)
|
if (has_name)
|
||||||
return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value_u, m_names[e]);
|
return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value_u, m_names[e]);
|
||||||
else
|
else
|
||||||
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u);
|
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */", value_u);
|
||||||
|
case 'n':
|
||||||
|
if (has_name)
|
||||||
|
return fmt::format_to(ctx.out(), "{}", m_names[e]);
|
||||||
|
else
|
||||||
|
return fmt::format_to(ctx.out(), "Invalid ({})", value_s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,5 +97,5 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const array_type m_names;
|
const array_type m_names;
|
||||||
bool formatting_for_shader = false;
|
char format_type = 'u';
|
||||||
};
|
};
|
||||||
|
@ -258,7 +258,7 @@ enum class TevBias : u32
|
|||||||
{
|
{
|
||||||
Zero = 0,
|
Zero = 0,
|
||||||
AddHalf = 1,
|
AddHalf = 1,
|
||||||
Subhalf = 2,
|
SubHalf = 2,
|
||||||
Compare = 3
|
Compare = 3
|
||||||
};
|
};
|
||||||
template <>
|
template <>
|
||||||
@ -491,6 +491,94 @@ struct fmt::formatter<TevStageCombiner::ColorCombiner>
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const TevStageCombiner::ColorCombiner& cc, FormatContext& ctx)
|
auto format(const TevStageCombiner::ColorCombiner& cc, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
|
auto out = ctx.out();
|
||||||
|
if (cc.bias != TevBias::Compare)
|
||||||
|
{
|
||||||
|
// Generate an equation view, simplifying out addition of zero and multiplication by 1
|
||||||
|
// dest = (d (OP) ((1 - c)*a + c*b) + bias) * scale
|
||||||
|
// or equivalently and more readably when the terms are not constants:
|
||||||
|
// dest = (d (OP) lerp(a, b, c) + bias) * scale
|
||||||
|
// Note that lerping is more complex than the first form shows; see PixelShaderGen's
|
||||||
|
// WriteTevRegular for more details.
|
||||||
|
|
||||||
|
static constexpr Common::EnumMap<const char*, TevColorArg::Zero> alt_names = {
|
||||||
|
"prev.rgb", "prev.aaa", "c0.rgb", "c0.aaa", "c1.rgb", "c1.aaa", "c2.rgb", "c2.aaa",
|
||||||
|
"tex.rgb", "tex.aaa", "ras.rgb", "ras.aaa", "1", ".5", "konst.rgb", "0",
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool has_d = cc.d != TevColorArg::Zero;
|
||||||
|
// If c is one, (1 - c) is zero, so (1-c)*a is zero
|
||||||
|
const bool has_ac = cc.a != TevColorArg::Zero && cc.c != TevColorArg::One;
|
||||||
|
// If either b or c is zero, b*c is zero
|
||||||
|
const bool has_bc = cc.b != TevColorArg::Zero && cc.c != TevColorArg::Zero;
|
||||||
|
const bool has_bias = cc.bias != TevBias::Zero; // != Compare is already known
|
||||||
|
const bool has_scale = cc.scale != TevScale::Scale1;
|
||||||
|
|
||||||
|
const char op = (cc.op == TevOp::Sub ? '-' : '+');
|
||||||
|
|
||||||
|
if (cc.dest == TevOutput::Prev)
|
||||||
|
out = format_to(out, "dest.rgb = ");
|
||||||
|
else
|
||||||
|
out = format_to(out, "{:n}.rgb = ", cc.dest);
|
||||||
|
|
||||||
|
if (has_scale)
|
||||||
|
out = format_to(out, "(");
|
||||||
|
if (has_d)
|
||||||
|
out = format_to(out, "{}", alt_names[cc.d]);
|
||||||
|
if (has_ac || has_bc)
|
||||||
|
{
|
||||||
|
if (has_d)
|
||||||
|
out = format_to(out, " {} ", op);
|
||||||
|
else if (cc.op == TevOp::Sub)
|
||||||
|
out = format_to(out, "{}", op);
|
||||||
|
if (has_ac && has_bc)
|
||||||
|
{
|
||||||
|
if (cc.c == TevColorArg::Half)
|
||||||
|
{
|
||||||
|
// has_a and has_b imply that c is not Zero or One, and Half is the only remaining
|
||||||
|
// numeric constant. This results in an average.
|
||||||
|
out = format_to(out, "({} + {})/2", alt_names[cc.a], alt_names[cc.b]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out = format_to(out, "lerp({}, {}, {})", alt_names[cc.a], alt_names[cc.b],
|
||||||
|
alt_names[cc.c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (has_ac)
|
||||||
|
{
|
||||||
|
if (cc.c == TevColorArg::Zero)
|
||||||
|
out = format_to(out, "{}", alt_names[cc.a]);
|
||||||
|
else if (cc.c == TevColorArg::Half) // 1 - .5 is .5
|
||||||
|
out = format_to(out, ".5*{}", alt_names[cc.a]);
|
||||||
|
else
|
||||||
|
out = format_to(out, "(1 - {})*{}", alt_names[cc.c], alt_names[cc.a]);
|
||||||
|
}
|
||||||
|
else // has_bc
|
||||||
|
{
|
||||||
|
if (cc.c == TevColorArg::One)
|
||||||
|
out = format_to(out, "{}", alt_names[cc.b]);
|
||||||
|
else
|
||||||
|
out = format_to(out, "{}*{}", alt_names[cc.c], alt_names[cc.b]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_bias)
|
||||||
|
{
|
||||||
|
if (has_ac || has_bc || has_d)
|
||||||
|
out = format_to(out, cc.bias == TevBias::AddHalf ? " + .5" : " - .5");
|
||||||
|
else
|
||||||
|
out = format_to(out, cc.bias == TevBias::AddHalf ? ".5" : "-.5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If nothing has been written so far, add a zero
|
||||||
|
if (!(has_ac || has_bc || has_d))
|
||||||
|
out = format_to(out, "0");
|
||||||
|
}
|
||||||
|
if (has_scale)
|
||||||
|
out = format_to(out, ") * {:n}", cc.scale);
|
||||||
|
out = format_to(out, "\n\n");
|
||||||
|
}
|
||||||
return format_to(ctx.out(),
|
return format_to(ctx.out(),
|
||||||
"a: {}\n"
|
"a: {}\n"
|
||||||
"b: {}\n"
|
"b: {}\n"
|
||||||
@ -512,7 +600,80 @@ struct fmt::formatter<TevStageCombiner::AlphaCombiner>
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const TevStageCombiner::AlphaCombiner& ac, FormatContext& ctx)
|
auto format(const TevStageCombiner::AlphaCombiner& ac, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
return format_to(ctx.out(),
|
auto out = ctx.out();
|
||||||
|
if (ac.bias != TevBias::Compare)
|
||||||
|
{
|
||||||
|
// Generate an equation view, simplifying out addition of zero and multiplication by 1
|
||||||
|
// dest = (d (OP) ((1 - c)*a + c*b) + bias) * scale
|
||||||
|
// or equivalently and more readably when the terms are not constants:
|
||||||
|
// dest = (d (OP) lerp(a, b, c) + bias) * scale
|
||||||
|
// Note that lerping is more complex than the first form shows; see PixelShaderGen's
|
||||||
|
// WriteTevRegular for more details.
|
||||||
|
|
||||||
|
// We don't need an alt_names map here, unlike the color combiner, as the only special term is
|
||||||
|
// Zero, and we we filter that out below. However, we do need to append ".a" to all
|
||||||
|
// parameters, to make it explicit that these are operations on the alpha term instead of the
|
||||||
|
// 4-element vector. We also need to use the :n specifier so that the numeric ID isn't shown.
|
||||||
|
|
||||||
|
const bool has_d = ac.d != TevAlphaArg::Zero;
|
||||||
|
// There is no c value for alpha that results in (1 - c) always being zero
|
||||||
|
const bool has_ac = ac.a != TevAlphaArg::Zero;
|
||||||
|
// If either b or c is zero, b*c is zero
|
||||||
|
const bool has_bc = ac.b != TevAlphaArg::Zero && ac.c != TevAlphaArg::Zero;
|
||||||
|
const bool has_bias = ac.bias != TevBias::Zero; // != Compare is already known
|
||||||
|
const bool has_scale = ac.scale != TevScale::Scale1;
|
||||||
|
|
||||||
|
const char op = (ac.op == TevOp::Sub ? '-' : '+');
|
||||||
|
|
||||||
|
if (ac.dest == TevOutput::Prev)
|
||||||
|
out = format_to(out, "dest.a = ");
|
||||||
|
else
|
||||||
|
out = format_to(out, "{:n}.a = ", ac.dest);
|
||||||
|
|
||||||
|
if (has_scale)
|
||||||
|
out = format_to(out, "(");
|
||||||
|
if (has_d)
|
||||||
|
out = format_to(out, "{:n}.a", ac.d);
|
||||||
|
if (has_ac || has_bc)
|
||||||
|
{
|
||||||
|
if (has_d)
|
||||||
|
out = format_to(out, " {} ", op);
|
||||||
|
else if (ac.op == TevOp::Sub)
|
||||||
|
out = format_to(out, "{}", op);
|
||||||
|
if (has_ac && has_bc)
|
||||||
|
{
|
||||||
|
out = format_to(out, "lerp({:n}.a, {:n}.a, {:n}.a)", ac.a, ac.b, ac.c);
|
||||||
|
}
|
||||||
|
else if (has_ac)
|
||||||
|
{
|
||||||
|
if (ac.c == TevAlphaArg::Zero)
|
||||||
|
out = format_to(out, "{:n}.a", ac.a);
|
||||||
|
else
|
||||||
|
out = format_to(out, "(1 - {:n}.a)*{:n}.a", ac.c, ac.a);
|
||||||
|
}
|
||||||
|
else // has_bc
|
||||||
|
{
|
||||||
|
out = format_to(out, "{:n}.a*{:n}.a", ac.c, ac.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has_bias)
|
||||||
|
{
|
||||||
|
if (has_ac || has_bc || has_d)
|
||||||
|
out = format_to(out, ac.bias == TevBias::AddHalf ? " + .5" : " - .5");
|
||||||
|
else
|
||||||
|
out = format_to(out, ac.bias == TevBias::AddHalf ? ".5" : "-.5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If nothing has been written so far, add a zero
|
||||||
|
if (!(has_ac || has_bc || has_d))
|
||||||
|
out = format_to(out, "0");
|
||||||
|
}
|
||||||
|
if (has_scale)
|
||||||
|
out = format_to(out, ") * {:n}", ac.scale);
|
||||||
|
out = format_to(out, "\n\n");
|
||||||
|
}
|
||||||
|
return format_to(out,
|
||||||
"a: {}\n"
|
"a: {}\n"
|
||||||
"b: {}\n"
|
"b: {}\n"
|
||||||
"c: {}\n"
|
"c: {}\n"
|
||||||
|
@ -46,6 +46,12 @@ TEST(EnumUtil, Enum1)
|
|||||||
EXPECT_EQ(fmt::format("{:s}", Enum1::C), "0x2u /* C */");
|
EXPECT_EQ(fmt::format("{:s}", Enum1::C), "0x2u /* C */");
|
||||||
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(3)), "0x3u /* Invalid */");
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(3)), "0x3u /* Invalid */");
|
||||||
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(4)), "0x4u /* Invalid */");
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(4)), "0x4u /* Invalid */");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum1::A), "A");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum1::B), "B");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum1::C), "C");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", static_cast<Enum1>(3)), "Invalid (3)");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", static_cast<Enum1>(4)), "Invalid (4)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EnumUtil, Enum2)
|
TEST(EnumUtil, Enum2)
|
||||||
@ -63,4 +69,11 @@ TEST(EnumUtil, Enum2)
|
|||||||
EXPECT_EQ(fmt::format("{:s}", Enum2::F), "0x3u /* F */");
|
EXPECT_EQ(fmt::format("{:s}", Enum2::F), "0x3u /* F */");
|
||||||
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(4)), "0x4u /* Invalid */");
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(4)), "0x4u /* Invalid */");
|
||||||
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(-1)), "0xffffffffu /* Invalid */");
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(-1)), "0xffffffffu /* Invalid */");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum2::D), "D");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum2::E), "E");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", static_cast<Enum2>(2)), "Invalid (2)");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", Enum2::F), "F");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", static_cast<Enum2>(4)), "Invalid (4)");
|
||||||
|
EXPECT_EQ(fmt::format("{:n}", static_cast<Enum2>(-1)), "Invalid (-1)");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user