GPU: Shader: Add support for enum class
In the process, refactor the enum processing using the new parser. Pull Request: https://projects.blender.org/blender/blender/pulls/144704
This commit is contained in:
@@ -73,7 +73,7 @@ enum VertexClass : uint32_t {
|
||||
ENUM_OPERATORS(VertexClass, VCLASS_EMPTY_SIZE)
|
||||
#endif
|
||||
|
||||
enum StickBoneFlag {
|
||||
enum StickBoneFlag : uint32_t {
|
||||
COL_WIRE = (1u << 0u),
|
||||
COL_HEAD = (1u << 1u),
|
||||
COL_TAIL = (1u << 2u),
|
||||
|
@@ -57,9 +57,6 @@
|
||||
# define ATTR_FALLTHROUGH
|
||||
# define ENUM_OPERATORS(a, b)
|
||||
# define UNUSED_VARS(a)
|
||||
/* Incompatible keywords. */
|
||||
# define static
|
||||
# define inline
|
||||
/* Math function renaming. */
|
||||
# define cosf cos
|
||||
# define sinf sin
|
||||
|
@@ -218,6 +218,7 @@ class Preprocessor {
|
||||
}
|
||||
str = preprocessor_directive_mutation(str);
|
||||
str = swizzle_function_mutation(str, report_error);
|
||||
str = enum_macro_injection(str, language == CPP, report_error);
|
||||
if (language == BLENDER_GLSL) {
|
||||
str = struct_method_mutation(str, report_error);
|
||||
str = method_call_mutation(str, report_error);
|
||||
@@ -247,7 +248,6 @@ class Preprocessor {
|
||||
str = variable_reference_mutation(str, report_error);
|
||||
str = template_definition_mutation(str, report_error);
|
||||
str = template_call_mutation(str, report_error);
|
||||
str = enum_macro_injection(str);
|
||||
}
|
||||
#ifdef __APPLE__ /* Limiting to Apple hardware since GLSL compilers might have issues. */
|
||||
if (language == GLSL) {
|
||||
@@ -1509,13 +1509,15 @@ class Preprocessor {
|
||||
return guarded_cope;
|
||||
}
|
||||
|
||||
std::string enum_macro_injection(std::string str)
|
||||
std::string enum_macro_injection(const std::string &str,
|
||||
bool is_shared_file,
|
||||
report_callback &report_error)
|
||||
{
|
||||
/**
|
||||
* Transform C,C++ enum declaration into GLSL compatible defines and constants:
|
||||
*
|
||||
* \code{.cpp}
|
||||
* enum eMyEnum : uint32_t {
|
||||
* enum eMyEnum : uint {
|
||||
* ENUM_1 = 0u,
|
||||
* ENUM_2 = 1u,
|
||||
* ENUM_3 = 2u,
|
||||
@@ -1525,11 +1527,11 @@ class Preprocessor {
|
||||
* becomes
|
||||
*
|
||||
* \code{.glsl}
|
||||
* _enum_decl(_eMyEnum)
|
||||
* ENUM_1 = 0u,
|
||||
* ENUM_2 = 1u,
|
||||
* ENUM_3 = 2u, _enum_end
|
||||
* #define eMyEnum _enum_type(_eMyEnum)
|
||||
* #define eMyEnum uint
|
||||
* constant static constexpr uint ENUM_1 = 0u;
|
||||
* constant static constexpr uint ENUM_2 = 1u;
|
||||
* constant static constexpr uint ENUM_3 = 2u;
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* It is made like so to avoid messing with error lines, allowing to point at the exact
|
||||
@@ -1540,20 +1542,73 @@ class Preprocessor {
|
||||
* - All values needs to be specified using constant literals to avoid compiler differences.
|
||||
* - All values needs to have the 'u' suffix to avoid GLSL compiler errors.
|
||||
*/
|
||||
{
|
||||
/* Replaces all matches by the respective string hash. */
|
||||
std::regex regex(R"(enum\s+((\w+)\s*(?:\:\s*\w+\s*)?)\{(\n[^}]+)\n\};)");
|
||||
str = std::regex_replace(str,
|
||||
regex,
|
||||
"_enum_decl(_$1)$3 _enum_end\n"
|
||||
"#define $2 _enum_type(_$2)");
|
||||
}
|
||||
{
|
||||
/* Remove trailing comma if any. */
|
||||
std::regex regex(R"(,(\s*_enum_end))");
|
||||
str = std::regex_replace(str, regex, "$1");
|
||||
}
|
||||
return str;
|
||||
using namespace std;
|
||||
using namespace shader::parser;
|
||||
|
||||
Parser parser(str, report_error);
|
||||
|
||||
auto missing_underlying_type = [&](vector<Token> tokens) {
|
||||
report_error(tokens[0].line_number(),
|
||||
tokens[0].char_number(),
|
||||
tokens[0].line_str(),
|
||||
"enum declaration must explicitly use an underlying type");
|
||||
};
|
||||
|
||||
parser.foreach_match("Mw{", missing_underlying_type);
|
||||
parser.foreach_match("MSw{", missing_underlying_type);
|
||||
|
||||
auto process_enum =
|
||||
[&](Token enum_tok, Token class_tok, Token enum_name, Token enum_type, Scope enum_scope) {
|
||||
string type_str = enum_type.str();
|
||||
|
||||
if (is_shared_file) {
|
||||
if (type_str != "uint32_t" && type_str != "int32_t") {
|
||||
report_error(
|
||||
enum_type.line_number(),
|
||||
enum_type.char_number(),
|
||||
enum_type.line_str(),
|
||||
"enum declaration must use uint32_t or int32_t underlying type for interface "
|
||||
"compatibility");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
size_t insert_at = enum_scope.end().line_end();
|
||||
parser.erase(enum_tok.str_index_start(), insert_at);
|
||||
parser.insert_line_number(insert_at + 1, enum_tok.line_number());
|
||||
parser.insert_after(insert_at + 1,
|
||||
"#define " + enum_name.str() + " " + enum_type.str() + "\n");
|
||||
|
||||
enum_scope.foreach_scope(ScopeType::Assignment, [&](Scope scope) {
|
||||
string name = scope.start().prev().str();
|
||||
string value = scope.str();
|
||||
if (class_tok.is_valid()) {
|
||||
name = enum_name.str() + "::" + name;
|
||||
}
|
||||
string decl = "constant static constexpr " + type_str + " " + name + " " + value +
|
||||
";\n";
|
||||
parser.insert_line_number(insert_at + 1, scope.start().line_number());
|
||||
parser.insert_after(insert_at + 1, decl);
|
||||
});
|
||||
parser.insert_line_number(insert_at + 1, enum_scope.end().line_number() + 1);
|
||||
};
|
||||
|
||||
parser.foreach_match("MSw:w{", [&](vector<Token> tokens) {
|
||||
process_enum(tokens[0], tokens[1], tokens[2], tokens[4], tokens[5].scope());
|
||||
});
|
||||
parser.foreach_match("Mw:w{", [&](vector<Token> tokens) {
|
||||
process_enum(tokens[0], Token::invalid(), tokens[1], tokens[3], tokens[4].scope());
|
||||
});
|
||||
|
||||
parser.apply_mutations();
|
||||
|
||||
parser.foreach_match("M", [&](vector<Token> tokens) {
|
||||
report_error(tokens[0].line_number(),
|
||||
tokens[0].char_number(),
|
||||
tokens[0].line_str(),
|
||||
"invalid enum declaration");
|
||||
});
|
||||
return parser.result_get();
|
||||
}
|
||||
|
||||
std::string strip_whitespace(const std::string &str) const
|
||||
|
@@ -376,6 +376,9 @@ struct ParserData {
|
||||
else if (word == "public") {
|
||||
c = Public;
|
||||
}
|
||||
else if (word == "enum") {
|
||||
c = Enum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -164,6 +164,14 @@ RESHAPE(float3x3, mat3x3, mat3x4)
|
||||
#define _enum_decl(name) constexpr uint
|
||||
#define _enum_end _enum_dummy;
|
||||
|
||||
/* Incompatible keywords. */
|
||||
#define static
|
||||
#define inline
|
||||
#define constant
|
||||
#define device
|
||||
#define thread
|
||||
#define threadgroup
|
||||
|
||||
/* Stage agnostic builtin function.
|
||||
* GLSL doesn't allow mixing shader stages inside the same source file.
|
||||
* Make sure builtin functions are stubbed when used in an invalid stage. */
|
||||
|
@@ -765,6 +765,45 @@ static void test_preprocess_swizzle()
|
||||
}
|
||||
GPU_TEST(preprocess_swizzle);
|
||||
|
||||
static void test_preprocess_enum()
|
||||
{
|
||||
using namespace shader;
|
||||
using namespace std;
|
||||
|
||||
{
|
||||
string input = R"(
|
||||
enum class enum_class : int {
|
||||
VALUE = 0,
|
||||
};
|
||||
)";
|
||||
string expect = R"(
|
||||
|
||||
|
||||
|
||||
#line 2
|
||||
#define enum_class int
|
||||
#line 3
|
||||
constant static constexpr int enum_class_VALUE = 0;
|
||||
#line 5
|
||||
)";
|
||||
string error;
|
||||
string output = process_test_string(input, error);
|
||||
EXPECT_EQ(output, expect);
|
||||
EXPECT_EQ(error, "");
|
||||
}
|
||||
{
|
||||
string input = R"(
|
||||
enum class enum_class {
|
||||
VALUE = 0,
|
||||
};
|
||||
)";
|
||||
string error;
|
||||
string output = process_test_string(input, error);
|
||||
EXPECT_EQ(error, "enum declaration must explicitly use an underlying type");
|
||||
}
|
||||
}
|
||||
GPU_TEST(preprocess_enum);
|
||||
|
||||
#ifdef __APPLE__ /* This processing is only done for metal compatibility. */
|
||||
static void test_preprocess_matrix_constructors()
|
||||
{
|
||||
|
Reference in New Issue
Block a user