Changeset View
Standalone View
source/blender/gpu/metal/mtl_shader_generator.mm
| Show First 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | else { | ||||||||||
| *len = numchars; | *len = numchars; | ||||||||||
| return (numchars > 0); | return (numchars > 0); | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| *len = numchars; | *len = numchars; | ||||||||||
| return true; | return true; | ||||||||||
| } | } | ||||||||||
| static int backwards_program_word_scan(const char *array_loc, const char *min) | |||||||||||
| { | |||||||||||
| const char *start; | |||||||||||
| char last_char = ' '; | |||||||||||
| int numchars = 0; | |||||||||||
| for (start = array_loc - 1; (start >= min) && (*start != '\0'); start--) { | |||||||||||
| char ch = *start; | |||||||||||
| if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || | |||||||||||
| ch == '_' || ch == '#') { | |||||||||||
| numchars++; | |||||||||||
| last_char = ch; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| break; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| if (numchars > 0) { | |||||||||||
| /* cannot start with numbers, so we need to invalidate the word. */ | |||||||||||
| if ((last_char >= '0' && last_char <= '9')) { | |||||||||||
| numchars = 0; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| return numchars; | |||||||||||
| } | |||||||||||
| /* Extract clipping distance usage indices, and replace syntax with metal-compatible. | |||||||||||
| * We need to replace syntax gl_ClipDistance[N] with gl_ClipDistance_N such that it is compatible | |||||||||||
| * with the Metal shaders Vertex shader output struct. */ | |||||||||||
| static void extract_and_replace_clipping_distances(std::string &vertex_source, | |||||||||||
| MSLGeneratorInterface &msl_iface) | |||||||||||
| { | |||||||||||
| char *current_str_begin = &*vertex_source.begin(); | |||||||||||
| char *current_str_end = &*vertex_source.end(); | |||||||||||
| for (char *c = current_str_begin + 2; c < current_str_end - 18; c++) { | |||||||||||
| char *base_search = strstr(c, "gl_ClipDistance["); | |||||||||||
| if (base_search == nullptr) { | |||||||||||
| /* No clip distances found. */ | |||||||||||
| return; | |||||||||||
| } | |||||||||||
| c = base_search + 16; | |||||||||||
fclemUnsubmitted Not Done Inline Actions
fclem: | |||||||||||
| /* Ensure closing brace. */ | |||||||||||
| if (*(c + 1) != ']') { | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| /* Extract ID betwen zero and 9. */ | |||||||||||
| if ((*c >= '0') && (*c <= '9')) { | |||||||||||
| char clip_distance_id = ((*c) - '0'); | |||||||||||
| auto found = std::find( | |||||||||||
| msl_iface.clip_distances.begin(), msl_iface.clip_distances.end(), clip_distance_id); | |||||||||||
| if (found == msl_iface.clip_distances.end()) { | |||||||||||
| msl_iface.clip_distances.append(clip_distance_id); | |||||||||||
| } | |||||||||||
| /* Replace syntax (array brace removal, and replacement with underscore). */ | |||||||||||
| *(base_search + 15) = '_'; | |||||||||||
| *(base_search + 17) = ' '; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| } | |||||||||||
| /** | /** | ||||||||||
| * Replace function parameter patterns containing: | * Replace function parameter patterns containing: | ||||||||||
| * `out vec3 somevar` with `THD vec3&somevar`. | * `out vec3 somevar` with `THD vec3&somevar`. | ||||||||||
| * which enables pass by reference via resolved macro: | * which enables pass by reference via resolved macro: | ||||||||||
| * `thread vec3& somevar`. | * `thread vec3& somevar`. | ||||||||||
| */ | */ | ||||||||||
| static void replace_outvars(std::string &str) | static void replace_outvars(std::string &str) | ||||||||||
| { | { | ||||||||||
| char *current_str_begin = &*str.begin(); | char *current_str_begin = &*str.begin(); | ||||||||||
| char *current_str_end = &*str.end(); | char *current_str_end = &*str.end(); | ||||||||||
| for (char *c = current_str_begin + 2; c < current_str_end - 6; c++) { | for (char *c = current_str_begin + 2; c < current_str_end - 6; c++) { | ||||||||||
| char *start = c; | char *start = strstr(c, "out"); | ||||||||||
| if (strncmp(c, "out ", 4) == 0) { | if (start == nullptr) { | ||||||||||
| return; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| c = start; | |||||||||||
| if (strncmp(c - 2, "in", 2) == 0) { | if (strncmp(c - 2, "in", 2) == 0) { | ||||||||||
| start = c - 2; | start = c - 2; | ||||||||||
| } | } | ||||||||||
| /* Check that the following are words. */ | /* Check that the following are words. */ | ||||||||||
| int len1, len2; | int len1, len2; | ||||||||||
| char *word_base1 = c + 4; | char *word_base1 = c + 4; | ||||||||||
| char *word_base2 = word_base1; | char *word_base2 = word_base1; | ||||||||||
| Show All 34 Lines | else { | ||||||||||
| *(word_base2 - 1) = '&'; | *(word_base2 - 1) = '&'; | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| static void replace_matrix_constructors(std::string &str) | |||||||||||
| { | |||||||||||
| /* Replace matrix constructors with GLSL-compatible constructors for Metal. | |||||||||||
| * Base matrix constructors e.g. mat3x3 do not have as many overload variants as GLSL. | |||||||||||
| * To add compatibility, we declare custom constuctors e.g. MAT3x3 in mtl_shader_defines.msl. | |||||||||||
| * If the GLSL syntax matches, we map mat3x3(..) -> MAT3x3(..) and implement a custom | |||||||||||
| * constructor. This supports both mat3(..) and mat3x3(..) style sytax.*/ | |||||||||||
| char *current_str_begin = &*str.begin(); | |||||||||||
| char *current_str_end = &*str.end(); | |||||||||||
| for (char *c = current_str_begin; c < current_str_end - 10; c++) { | |||||||||||
| char *base_scan = strstr(c, "mat"); | |||||||||||
| if (base_scan == nullptr) { | |||||||||||
| break; | |||||||||||
| } | |||||||||||
| /* Track end of constructor. */ | |||||||||||
| char *constructor_end = nullptr; | |||||||||||
| /* check if next character is matrix dim. */ | |||||||||||
| c = base_scan + 3; | |||||||||||
| if (!(*c == '2' || *c == '3' || *c == '4')) { | |||||||||||
| /* Not constructor, skip. */ | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| /* Possible multiple dimensional matrix constructor. Verify if next char is a dim*/ | |||||||||||
| c++; | |||||||||||
| if (*c == 'x') { | |||||||||||
| c++; | |||||||||||
| if (*c == '2' || *c == '3' || *c == '4') { | |||||||||||
| c++; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| /* Not matrix constructor, continue. */ | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| /* Check for constructor opening brace. */ | |||||||||||
| if (*c == '(') { | |||||||||||
| constructor_end = c; | |||||||||||
| } | |||||||||||
| else { | |||||||||||
| /* Not matrix constructor, continue. */ | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| /* If is constructor, replace with MATN(..) syntax. */ | |||||||||||
| if (constructor_end != nullptr) { | |||||||||||
| strncpy(base_scan, "MAT", 3); | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| } | |||||||||||
| } | |||||||||||
| static void replace_array_initializers_func(std::string &str) | static void replace_array_initializers_func(std::string &str) | ||||||||||
| { | { | ||||||||||
| char *current_str_begin = &*str.begin(); | char *current_str_begin = &*str.begin(); | ||||||||||
| char *current_str_end = &*str.end(); | char *current_str_end = &*str.end(); | ||||||||||
| for (char *c = current_str_begin; c < current_str_end - 6; c++) { | for (char *c = current_str_begin; c < current_str_end - 6; c++) { | ||||||||||
| char *base_scan = c; | |||||||||||
| int typelen = 0; | int typelen = 0; | ||||||||||
| if (is_program_word(c, &typelen) && *(c + typelen) == '[') { | /* first find next array brace, then work backwards to find start of program word to check if | ||||||||||
| * valid array syntax. */ | |||||||||||
| char *array_scan = strchr(c, '['); | |||||||||||
| if (array_scan == nullptr) { | |||||||||||
| return; | |||||||||||
| } | |||||||||||
| typelen = backwards_program_word_scan(array_scan - 1, current_str_begin); | |||||||||||
| char *base_type_name = array_scan - 1 - typelen; | |||||||||||
| char *array_len_start = c + typelen + 1; | if (typelen > 0) { | ||||||||||
| c = array_len_start; | // if (is_program_word(c, &typelen) && *(c + typelen) == '[') { | ||||||||||
Not Done Inline Actionsleft over comment fclem: left over comment | |||||||||||
| c = array_scan; | |||||||||||
| char *closing_square_brace = strchr(c, ']'); | char *closing_square_brace = strchr(c, ']'); | ||||||||||
| if (closing_square_brace != nullptr) { | if (closing_square_brace != nullptr) { | ||||||||||
| c = closing_square_brace; | c = closing_square_brace; | ||||||||||
| char *first_bracket = c + 1; | char *first_bracket = c + 1; | ||||||||||
| if (*first_bracket == '(') { | if (*first_bracket == '(') { | ||||||||||
| c += 1; | c += 1; | ||||||||||
| char *semi_colon = strchr(c, ';'); | char *semi_colon = strchr(c, ';'); | ||||||||||
| if (semi_colon != nullptr && *(semi_colon - 1) == ')') { | if (semi_colon != nullptr && *(semi_colon - 1) == ')') { | ||||||||||
| char *closing_bracket = semi_colon - 1; | char *closing_bracket = semi_colon - 1; | ||||||||||
| /* Resolve to MSL-compatible array formatting. */ | /* Resolve to MSL-compatible array formatting. */ | ||||||||||
| *first_bracket = '{'; | *first_bracket = '{'; | ||||||||||
| *closing_bracket = '}'; | *closing_bracket = '}'; | ||||||||||
| for (char *clear = base_scan; clear <= closing_square_brace; clear++) { | for (char *clear = base_type_name; clear <= closing_square_brace; clear++) { | ||||||||||
| *clear = ' '; | *clear = ' '; | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| else { | else { | ||||||||||
| return; | return; | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| else { | |||||||||||
| /* Not an array initializer, continue scanning. */ | |||||||||||
| c = array_scan + 1; | |||||||||||
| continue; | |||||||||||
| } | |||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| #ifndef NDEBUG | #ifndef NDEBUG | ||||||||||
| static bool balanced_braces(char *current_str_begin, char *current_str_end) | static bool balanced_braces(char *current_str_begin, char *current_str_end) | ||||||||||
| { | { | ||||||||||
| int nested_bracket_depth = 0; | int nested_bracket_depth = 0; | ||||||||||
| Show All 36 Lines | if (*c == '}' || *c == ']' || *c == ')') { | ||||||||||
| nested_bracket_depth--; | nested_bracket_depth--; | ||||||||||
| BLI_assert(nested_bracket_depth >= 0); | BLI_assert(nested_bracket_depth >= 0); | ||||||||||
| continue; | continue; | ||||||||||
| } | } | ||||||||||
| /* Check For global const declarations */ | /* Check For global const declarations */ | ||||||||||
| if (nested_bracket_depth == 0 && strncmp(c, "const ", 6) == 0 && | if (nested_bracket_depth == 0 && strncmp(c, "const ", 6) == 0 && | ||||||||||
| strncmp(c, "const constant ", 15) != 0) { | strncmp(c, "const constant ", 15) != 0) { | ||||||||||
| char *c_expr_end = strstr(c, ";"); | char *c_expr_end = strchr(c, ';'); | ||||||||||
| if (c_expr_end != nullptr && balanced_braces(c, c_expr_end)) { | if (c_expr_end != nullptr && balanced_braces(c, c_expr_end)) { | ||||||||||
| MTL_LOG_INFO( | MTL_LOG_INFO( | ||||||||||
| "[PERFORMANCE WARNING] Global scope constant expression found - These get allocated " | "[PERFORMANCE WARNING] Global scope constant expression found - These get allocated " | ||||||||||
| "per-thread in METAL - Best to use Macro's or uniforms to avoid overhead: '%.*s'\n", | "per-thread in METAL - Best to use Macro's or uniforms to avoid overhead: '%.*s'\n", | ||||||||||
Not Done Inline Actionsunrelated to this diff, but couldn't we pay this cost by just defining them as constexpr? fclem: unrelated to this diff, but couldn't we pay this cost by just defining them as `constexpr`? | |||||||||||
Not Done Inline ActionsI do have a patch coming in soon which addresses a few more of these cases. But to provide a bit of context, this is mostly a compiler issue specific to Metal, rather than a fundamental problem with how the code "should" work. Metal Shading Language doesn't have a concept of Global-scope within shaders, which means that certain global variables as in GL cannot be declared. Constant globals are allowed, however, in order to emulate GLSL globals, the Metal generated shader wraps a class around the entire GLSL implementation, such that it creates an effective global scope through class members. There is a compiler issue here where while expressions remain constant, for array/matrix types, due to the ability to perform random access on these, which cannot be resolved at compile time , the compiler allocates them within thread local memory using a chunk of the temporary register file. The performance impact comes when the local memory requirements for the shader exceed the reasonable limit available for each shader core, causing a decrease in occupancy, as memory has to be split across several cores per execution instance. (In the worst case, this can result in a 1/16th of the total performance). There is a secondary caveat that the compiler also cannot handle constexpr within a class, and instead must use static constexpr, but due to another limitation, using static constexpr can often be invalid in Metal, due to the requirement to declare a scope for variables. This should not be the case, but moving constant declarations within function scope is the least impactful way to work-around this. One other option would be to hoist constant expressions outside of the class wrapper, but this would require more complex parsing and increase shader translation time. Certainly not ideal, and hopefully something which could be done more cleanly in the future. So TL;DR unfortunately, constexpr does not work as expected due to memory scope (thread, threadgroup, device, constant) being incompatible with static constexpr within the shader's class body, due to how the generated Metal shaders wrap the GLSL implementation. MichaelPW: I do have a patch coming in soon which addresses a few more of these cases. But to provide a… | |||||||||||
| (int)(c_expr_end + 1 - c), | (int)(c_expr_end + 1 - c), | ||||||||||
| c); | c); | ||||||||||
| /* Jump ptr forward as we know we remain in global scope. */ | /* Jump ptr forward as we know we remain in global scope. */ | ||||||||||
| c = c_expr_end - 1; | c = c_expr_end - 1; | ||||||||||
| continue; | continue; | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| } | } | ||||||||||
| #endif | #endif | ||||||||||
| static bool extract_ssbo_pragma_info(const MTLShader *shader, | static bool extract_ssbo_pragma_info(const MTLShader *shader, | ||||||||||
| const MSLGeneratorInterface &, | const MSLGeneratorInterface &msl_iface, | ||||||||||
| const std::string &in_vertex_src, | const std::string &in_vertex_src, | ||||||||||
| MTLPrimitiveType &out_prim_tye, | MTLPrimitiveType &out_prim_tye, | ||||||||||
| uint32_t &out_num_output_verts) | uint32_t &out_num_output_verts) | ||||||||||
| { | { | ||||||||||
| /* SSBO Vertex-fetch parameter extraction. */ | /* SSBO Vertex-fetch parameter extraction. */ | ||||||||||
| static std::regex use_ssbo_fetch_mode_find( | static std::regex use_ssbo_fetch_mode_find( | ||||||||||
| "#pragma " | "#pragma " | ||||||||||
| "USE_SSBO_VERTEX_FETCH\\(\\s*(TriangleList|LineList|TriangleStrip|\\w+)\\s*,\\s*([0-9]+)\\s*" | "USE_SSBO_VERTEX_FETCH\\(\\s*(TriangleList|LineList|TriangleStrip|\\w+)\\s*,\\s*([0-9]+)\\s*" | ||||||||||
| ▲ Show 20 Lines • Show All 403 Lines • ▼ Show 20 Lines | if (msl_iface.uses_ssbo_vertex_fetch_mode) { | ||||||||||
| shader_debug_printf( | shader_debug_printf( | ||||||||||
| "[Shader] SSBO VERTEX FETCH Enabled for Shader '%s' With Output primitive type: %s, " | "[Shader] SSBO VERTEX FETCH Enabled for Shader '%s' With Output primitive type: %s, " | ||||||||||
| "vertex count: %u\n", | "vertex count: %u\n", | ||||||||||
| this->name_get(), | this->name_get(), | ||||||||||
| output_primitive_type.c_str(), | output_primitive_type.c_str(), | ||||||||||
| vertex_fetch_ssbo_num_output_verts); | vertex_fetch_ssbo_num_output_verts); | ||||||||||
| } | } | ||||||||||
| /*** Regex Commands ***/ | /* Special condition - mat3 and array constructor replacement. */ | ||||||||||
| /* Source cleanup and syntax replacement. */ | replace_matrix_constructors(shd_builder_->glsl_vertex_source_); | ||||||||||
| static std::regex remove_excess_newlines("\\n+"); | |||||||||||
| static std::regex replace_matrix_construct("mat([234](x[234])?)\\s*\\("); | |||||||||||
| /* Special condition - mat3 and array constructor replacement. | |||||||||||
| * Also replace excessive new lines to ensure cases are not missed. | |||||||||||
| * NOTE(Metal): May be able to skip excess-newline removal. */ | |||||||||||
| shd_builder_->glsl_vertex_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_vertex_source_, remove_excess_newlines, "\n"); | |||||||||||
| shd_builder_->glsl_vertex_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_vertex_source_, replace_matrix_construct, "MAT$1("); | |||||||||||
| replace_array_initializers_func(shd_builder_->glsl_vertex_source_); | replace_array_initializers_func(shd_builder_->glsl_vertex_source_); | ||||||||||
| if (!msl_iface.uses_transform_feedback) { | if (!msl_iface.uses_transform_feedback) { | ||||||||||
| shd_builder_->glsl_fragment_source_ = std::regex_replace( | replace_matrix_constructors(shd_builder_->glsl_fragment_source_); | ||||||||||
| shd_builder_->glsl_fragment_source_, remove_excess_newlines, "\n"); | |||||||||||
| shd_builder_->glsl_fragment_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_fragment_source_, replace_matrix_construct, "MAT$1("); | |||||||||||
| replace_array_initializers_func(shd_builder_->glsl_fragment_source_); | replace_array_initializers_func(shd_builder_->glsl_fragment_source_); | ||||||||||
| } | } | ||||||||||
| /**** Extract usage of GL globals. ****/ | /**** Extract usage of GL globals. ****/ | ||||||||||
| /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does | /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does | ||||||||||
| * not always contain the usage flag. This can be removed once all appropriate create-info's | * not always contain the usage flag. This can be removed once all appropriate create-info's | ||||||||||
| * have been updated. In some cases, this may incur a false positive if access is guarded | * have been updated. In some cases, this may incur a false positive if access is guarded | ||||||||||
| * behind a macro. Though in these cases, unused code paths and parameters will be | * behind a macro. Though in these cases, unused code paths and parameters will be | ||||||||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info) | ||||||||||
| } | } | ||||||||||
| /* Generate SSBO vertex fetch mode uniform data hooks. */ | /* Generate SSBO vertex fetch mode uniform data hooks. */ | ||||||||||
| if (msl_iface.uses_ssbo_vertex_fetch_mode) { | if (msl_iface.uses_ssbo_vertex_fetch_mode) { | ||||||||||
| msl_iface.prepare_ssbo_vertex_fetch_uniforms(); | msl_iface.prepare_ssbo_vertex_fetch_uniforms(); | ||||||||||
| } | } | ||||||||||
| /* Extract gl_ClipDistances. */ | /* Extract gl_ClipDistances. */ | ||||||||||
| static std::regex gl_clipdistance_find("gl_ClipDistance\\[([0-9])\\]"); | extract_and_replace_clipping_distances(shd_builder_->glsl_vertex_source_, msl_iface); | ||||||||||
| std::string clip_search_str = shd_builder_->glsl_vertex_source_; | |||||||||||
| std::smatch vertex_clip_distances; | |||||||||||
| while (std::regex_search(clip_search_str, vertex_clip_distances, gl_clipdistance_find)) { | |||||||||||
| shader_debug_printf("VERTEX CLIP DISTANCES FOUND: str: %s\n", | |||||||||||
| vertex_clip_distances[1].str().c_str()); | |||||||||||
| auto found = std::find(msl_iface.clip_distances.begin(), | |||||||||||
| msl_iface.clip_distances.end(), | |||||||||||
| vertex_clip_distances[1].str()); | |||||||||||
| if (found == msl_iface.clip_distances.end()) { | |||||||||||
| msl_iface.clip_distances.append(vertex_clip_distances[1].str()); | |||||||||||
| } | |||||||||||
| clip_search_str = vertex_clip_distances.suffix(); | |||||||||||
| } | |||||||||||
| shd_builder_->glsl_vertex_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_vertex_source_, gl_clipdistance_find, "gl_ClipDistance_$1"); | |||||||||||
| /* Replace 'out' attribute on function parameters with pass-by-reference. */ | /* Replace 'out' attribute on function parameters with pass-by-reference. */ | ||||||||||
| replace_outvars(shd_builder_->glsl_vertex_source_); | replace_outvars(shd_builder_->glsl_vertex_source_); | ||||||||||
| if (!msl_iface.uses_transform_feedback) { | if (!msl_iface.uses_transform_feedback) { | ||||||||||
| replace_outvars(shd_builder_->glsl_fragment_source_); | replace_outvars(shd_builder_->glsl_fragment_source_); | ||||||||||
| } | } | ||||||||||
| /**** METAL Shader source generation. ****/ | /**** METAL Shader source generation. ****/ | ||||||||||
| ▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) { | ||||||||||
| /* Generate local variables, populate elems for vertex out struct gen. */ | /* Generate local variables, populate elems for vertex out struct gen. */ | ||||||||||
| for (const StageInterfaceInfo::InOut &inout : iface->inouts) { | for (const StageInterfaceInfo::InOut &inout : iface->inouts) { | ||||||||||
| /* Only output individual elements if they are not part of an interface struct instance. */ | /* Only output individual elements if they are not part of an interface struct instance. */ | ||||||||||
| if (!is_inside_struct) { | if (!is_inside_struct) { | ||||||||||
| ss_vertex << to_string(inout.type) << " " << inout.name << ";" << std::endl; | ss_vertex << to_string(inout.type) << " " << inout.name << ";" << std::endl; | ||||||||||
| } | } | ||||||||||
| const char *arraystart = strstr(inout.name.c_str(), "["); | const char *arraystart = strchr(inout.name.c_str(), '['); | ||||||||||
| bool is_array = (arraystart != nullptr); | bool is_array = (arraystart != nullptr); | ||||||||||
| int array_len = (is_array) ? std::stoi(std::regex_replace( | int array_len = (is_array) ? std::stoi(std::regex_replace( | ||||||||||
| arraystart, remove_non_numeric_characters, "")) : | arraystart, remove_non_numeric_characters, "")) : | ||||||||||
| 0; | 0; | ||||||||||
| /* Remove array from string name. */ | /* Remove array from string name. */ | ||||||||||
| std::string out_name = inout.name.c_str(); | std::string out_name = inout.name.c_str(); | ||||||||||
| std::size_t pos = out_name.find('['); | std::size_t pos = out_name.find('['); | ||||||||||
| ▲ Show 20 Lines • Show All 274 Lines • ▼ Show 20 Lines | bool MTLShader::generate_msl_from_glsl_compute(const shader::ShaderCreateInfo *info) | ||||||||||
| /* Populate #MSLGeneratorInterface from Create-Info. | /* Populate #MSLGeneratorInterface from Create-Info. | ||||||||||
| * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated | * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated | ||||||||||
| * from parsing, if support for shaders without create-info is required. */ | * from parsing, if support for shaders without create-info is required. */ | ||||||||||
| msl_iface.prepare_from_createinfo(info); | msl_iface.prepare_from_createinfo(info); | ||||||||||
| /* Verify Source sizes are greater than zero. */ | /* Verify Source sizes are greater than zero. */ | ||||||||||
| BLI_assert(shd_builder_->glsl_compute_source_.size() > 0); | BLI_assert(shd_builder_->glsl_compute_source_.size() > 0); | ||||||||||
| /*** Regex Commands ***/ | /*** Source cleanup. ***/ | ||||||||||
| /* Source cleanup and syntax replacement. */ | replace_matrix_constructors(shd_builder_->glsl_compute_source_); | ||||||||||
| static std::regex remove_excess_newlines("\\n+"); | |||||||||||
| static std::regex replace_matrix_construct("mat([234](x[234])?)\\s*\\("); | |||||||||||
| /* Special condition - mat3 and array constructor replacement. | |||||||||||
| * Also replace excessive new lines to ensure cases are not missed. */ | |||||||||||
| shd_builder_->glsl_compute_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_compute_source_, remove_excess_newlines, "\n"); | |||||||||||
| shd_builder_->glsl_compute_source_ = std::regex_replace( | |||||||||||
| shd_builder_->glsl_compute_source_, replace_matrix_construct, "MAT$1("); | |||||||||||
| replace_array_initializers_func(shd_builder_->glsl_compute_source_); | replace_array_initializers_func(shd_builder_->glsl_compute_source_); | ||||||||||
| /**** Extract usage of GL globals. ****/ | /**** Extract usage of GL globals. ****/ | ||||||||||
| /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does | /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does | ||||||||||
| * not always contain the usage flag. This can be removed once all appropriate create-info's | * not always contain the usage flag. This can be removed once all appropriate create-info's | ||||||||||
| * have been updated. In some cases, this may incur a false positive if access is guarded | * have been updated. In some cases, this may incur a false positive if access is guarded | ||||||||||
| * behind a macro. Though in these cases, unused code paths and parameters will be | * behind a macro. Though in these cases, unused code paths and parameters will be | ||||||||||
| * optimized out by the Metal shader compiler. */ | * optimized out by the Metal shader compiler. */ | ||||||||||
| ▲ Show 20 Lines • Show All 2,336 Lines • Show Last 20 Lines | |||||||||||