def _process_shader_template(self, template_src, cache_key, injections):  # noqa # pylint: disable=too-many-branches
        """ Generates a compiled shader object from a given shader
        source location and code injection definitions. """
        with open(template_src, "r") as handle:
            shader_lines = handle.readlines()

        parsed_lines = ["\n\n"]
        addline = parsed_lines.append

        addline("/* Compiled Shader Template")
        addline(" * generated from: '" + template_src + "'")
        addline(" * cache key: '" + cache_key + "'")
        addline(" *")
        addline(" * !!! Autogenerated, do not edit! Your changes will be lost. !!!")
        addline(" */\n\n")

        # Store whether we are in the main function already - we need this
        # to properly insert scoped code blocks
        in_main = False

        for line in shader_lines:  # pylint: disable=too-many-nested-blocks
            stripped_line = line.strip().lower()

            # Check if we are already in the main function
            if "void main()" in stripped_line:
                in_main = True

            # Check if the current line is a hook
            if stripped_line.startswith("%") and stripped_line.endswith("%"):

                # If the line is a hook, get the hook name and save the
                # indent so we can indent all injected lines properly.
                hook_name = stripped_line[1:-1]
                indent = " " * (len(line) - len(line.lstrip()))

                # Inject all registered template values into the hook
                if hook_name in injections:

                    # Directly remove the value from the list so we can check which
                    # hooks were not found in the template
                    insertions = injections.pop(hook_name)

                    if len(insertions) > 0:

                        # When we are in the main function, we have to make sure we
                        # use a seperate scope, so there are no conflicts with variable
                        # declarations
                        header = indent + "/* Hook: " + hook_name + " */" + (" {" if in_main else "")  # noqa # pylint: disable=line-too-long
                        addline(header)

                        for line_to_insert in insertions:
                            if line_to_insert is None:
                                self.warn("Empty insertion '" + hook_name + "'")
                                continue

                            if not isinstance(line_to_insert, str):
                                self.warn("Invalid line type: ", line_to_insert)
                                continue

                            # Dont indent defines and pragmas
                            if line_to_insert.startswith("#"):
                                addline(line_to_insert)
                            else:
                                addline(indent + line_to_insert)

                        if in_main:
                            addline(indent + "}")

            else:
                addline(line.rstrip())

        # Add a closing newline to the file
        addline("")

        # Warn the user about all unused hooks
        for key in injections:
            self.warn("Hook '" + key + "' not found in template '" + template_src + "'!")

        # Write the constructed shader and load it back
        shader_content = "\n".join(parsed_lines)
        temp_path = "/$$rptemp/$$effect-" + cache_key + ".glsl"

        with open(temp_path, "w") as handle:
            handle.write(shader_content)

        return temp_path
 Generates a compiled shader object from a given shader
        source location and code injection definitions. 
metadrive/render_pipeline/rpcore/effect.py