From 327126d1e86c5b923aa4f0238b9ac983d1b761d9 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Thu, 18 Nov 2021 12:56:25 -0800 Subject: [PATCH] ShaderGenCommon: Add WriteSwitch --- Source/Core/VideoCommon/ShaderGenCommon.h | 61 ++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h index 367a472294..88a303356b 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.h +++ b/Source/Core/VideoCommon/ShaderGenCommon.h @@ -13,10 +13,11 @@ #include "Common/BitField.h" #include "Common/CommonTypes.h" +#include "Common/EnumMap.h" #include "Common/StringUtil.h" #include "Common/TypeUtils.h" -enum class APIType; +#include "VideoCommon/VideoCommon.h" /** * Common interface for classes that need to go through the shader generation path @@ -210,6 +211,64 @@ std::string BitfieldExtract(std::string_view source) static_cast(BitFieldT::NumBits())); } +template +void WriteSwitch(ShaderCode& out, APIType ApiType, std::string_view variable, + const Common::EnumMap& values, int indent, + bool break_) +{ + const bool make_switch = (ApiType == APIType::D3D); + + // The second template argument is needed to avoid compile errors from ambiguity with multiple + // enums with the same number of members in GCC prior to 8. See https://godbolt.org/z/xcKaW1seW + // and https://godbolt.org/z/hz7Yqq1P5 + using enum_type = decltype(last_member); + + // {:{}} is used to indent by formatting an empty string with a variable width + if (make_switch) + { + out.Write("{:{}}switch ({}) {{\n", "", indent, variable); + for (u32 i = 0; i <= static_cast(last_member); i++) + { + const enum_type key = static_cast(i); + + // Assumes existence of an EnumFormatter + out.Write("{:{}}case {:s}:\n", "", indent, key); + // Note that this indentation behaves poorly for multi-line code + if (!values[key].empty()) + out.Write("{:{}} {}\n", "", indent, values[key]); + if (break_) + out.Write("{:{}} break;\n", "", indent); + } + out.Write("{:{}}}}\n", "", indent); + } + else + { + // Generate a tree of if statements recursively + // std::function must be used because auto won't capture before initialization and thus can't be + // used recursively + std::function BuildTree = [&](u32 cur_indent, u32 low, u32 high) { + // Each generated statement is for low <= x < high + if (high == low + 1) + { + // Down to 1 case (low <= x < low + 1 means x == low) + const enum_type key = static_cast(low); + // Note that this indentation behaves poorly for multi-line code + out.Write("{:{}}{} // {}\n", "", cur_indent, values[key], key); + } + else + { + u32 mid = low + ((high - low) / 2); + out.Write("{:{}}if ({} < {}u) {{\n", "", cur_indent, variable, mid); + BuildTree(cur_indent + 2, low, mid); + out.Write("{:{}}}} else {{\n", "", cur_indent); + BuildTree(cur_indent + 2, mid, high); + out.Write("{:{}}}}\n", "", cur_indent); + } + }; + BuildTree(indent, 0, static_cast(last_member) + 1); + } +} + // Constant variable names #define I_COLORS "color" #define I_KCOLORS "k"