diff --git a/source/blender/draw/engines/overlay/overlay_shader_shared.hh b/source/blender/draw/engines/overlay/overlay_shader_shared.hh index 84a3d5c09ae..460e1b96cdd 100644 --- a/source/blender/draw/engines/overlay/overlay_shader_shared.hh +++ b/source/blender/draw/engines/overlay/overlay_shader_shared.hh @@ -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), diff --git a/source/blender/gpu/GPU_shader_shared_utils.hh b/source/blender/gpu/GPU_shader_shared_utils.hh index 9634d973b16..3bd4054a723 100644 --- a/source/blender/gpu/GPU_shader_shared_utils.hh +++ b/source/blender/gpu/GPU_shader_shared_utils.hh @@ -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 diff --git a/source/blender/gpu/glsl_preprocess/glsl_preprocess.hh b/source/blender/gpu/glsl_preprocess/glsl_preprocess.hh index 9ff764dbec9..cdabf684ae9 100644 --- a/source/blender/gpu/glsl_preprocess/glsl_preprocess.hh +++ b/source/blender/gpu/glsl_preprocess/glsl_preprocess.hh @@ -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 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 tokens) { + process_enum(tokens[0], tokens[1], tokens[2], tokens[4], tokens[5].scope()); + }); + parser.foreach_match("Mw:w{", [&](vector tokens) { + process_enum(tokens[0], Token::invalid(), tokens[1], tokens[3], tokens[4].scope()); + }); + + parser.apply_mutations(); + + parser.foreach_match("M", [&](vector 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 diff --git a/source/blender/gpu/glsl_preprocess/shader_parser.hh b/source/blender/gpu/glsl_preprocess/shader_parser.hh index 8dde3020072..96bfb2636eb 100644 --- a/source/blender/gpu/glsl_preprocess/shader_parser.hh +++ b/source/blender/gpu/glsl_preprocess/shader_parser.hh @@ -376,6 +376,9 @@ struct ParserData { else if (word == "public") { c = Public; } + else if (word == "enum") { + c = Enum; + } } } } diff --git a/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl b/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl index babb98a0416..a12bbecd048 100644 --- a/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl +++ b/source/blender/gpu/shaders/opengl/glsl_shader_defines.glsl @@ -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. */ diff --git a/source/blender/gpu/tests/shader_preprocess_test.cc b/source/blender/gpu/tests/shader_preprocess_test.cc index f469abb1a9c..da9dcb4e96d 100644 --- a/source/blender/gpu/tests/shader_preprocess_test.cc +++ b/source/blender/gpu/tests/shader_preprocess_test.cc @@ -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() {