From de72bbf3a4422be10dc72b225253e749cad84ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Tue, 7 Sep 2021 23:25:54 +0200 Subject: [PATCH] Cleanup arm.lib.make_datas --- blender/arm/lib/make_datas.py | 207 +++++++++++++++++----------------- 1 file changed, 104 insertions(+), 103 deletions(-) diff --git a/blender/arm/lib/make_datas.py b/blender/arm/lib/make_datas.py index 7a4618ad..7a8c61a0 100644 --- a/blender/arm/lib/make_datas.py +++ b/blender/arm/lib/make_datas.py @@ -1,14 +1,15 @@ -import os import arm.utils import arm.assets as assets -def parse_context(c, sres, asset, defs, vert=None, frag=None): - con = {} + +def parse_context(c: dict, sres: dict, asset, defs: list[str], vert: list[str] = None, frag: list[str] = None): + con = { + 'name': c['name'], + 'constants': [], + 'texture_units': [], + 'vertex_elements': [] + } sres['contexts'].append(con) - con['name'] = c['name'] - con['constants'] = [] - con['texture_units'] = [] - con['vertex_elements'] = [] # Names con['vertex_shader'] = c['vertex_shader'].rsplit('.', 1)[0].split('/')[-1] @@ -52,12 +53,12 @@ def parse_context(c, sres, asset, defs, vert=None, frag=None): con[p] = c[p] # Parse shaders - if vert == None: + if vert is None: with open(c['vertex_shader']) as f: vert = f.read().splitlines() parse_shader(sres, c, con, defs, vert, True) # Parse attribs for vertex shader - if frag == None: + if frag is None: with open(c['fragment_shader']) as f: frag = f.read().splitlines() parse_shader(sres, c, con, defs, frag, False) @@ -77,62 +78,76 @@ def parse_context(c, sres, asset, defs, vert=None, frag=None): tese = f.read().splitlines() parse_shader(sres, c, con, defs, tese, False) -def parse_shader(sres, c, con, defs, lines, parse_attributes): - skip_till_endif = 0 - skip_else = False + +def parse_shader(sres, c: dict, con: dict, defs: list[str], lines: list[str], parse_attributes: bool): + """Parses the given shader to get information about the used vertex + elements, uniforms and constants. This information is later used in + Iron to check what data each shader requires. + + @param defs A list of set defines for the preprocessor + @param lines The list of lines of the shader file + @param parse_attributes Whether to parse vertex elements + """ vertex_elements_parsed = False vertex_elements_parsing = False - stack = [] + # Stack of the state of all preprocessor conditions for the current + # line. If there is a `False` in the stack, at least one surrounding + # condition is false and the line must not be parsed + stack: list[bool] = [] - if parse_attributes == False: + if not parse_attributes: vertex_elements_parsed = True for line in lines: line = line.lstrip() # Preprocessor - if line.startswith('#if'): # if, ifdef, ifndef + if line.startswith('#if'): # if, ifdef, ifndef s = line.split(' ')[1] found = s in defs if line.startswith('#ifndef'): found = not found - if found == False: - stack.append(0) - else: - stack.append(1) + stack.append(found) continue if line.startswith('#else'): - stack[-1] = 1 - stack[-1] + stack[-1] = not stack[-1] continue if line.startswith('#endif'): stack.pop() continue + # Skip lines if the stack contains at least one preprocessor + # condition that is not fulfilled skip = False - for i in stack: - if i == 0: + for condition in stack: + if not condition: skip = True break if skip: continue - if vertex_elements_parsed == False and line.startswith('in '): + if not vertex_elements_parsed and line.startswith('in '): vertex_elements_parsing = True - vd = {} s = line.split(' ') - vd['data'] = 'float' + s[1][-1:] - vd['name'] = s[2][:-1] - con['vertex_elements'].append(vd) - if vertex_elements_parsing == True and len(line) > 0 and line.startswith('//') == False and line.startswith('in ') == False: + con['vertex_elements'].append({ + 'data': 'float' + s[1][-1:], + 'name': s[2][:-1] # [:1] to get rid of the semicolon + }) + + # Stop the vertex element parsing if no other vertex elements + # follow directly (assuming all vertex elements are positioned + # directly after each other apart from empty lines and comments) + if vertex_elements_parsing and len(line) > 0 and not line.startswith('//') and not line.startswith('in '): vertex_elements_parsed = True if line.startswith('uniform ') or line.startswith('//!uniform'): # Uniforms included from header files s = line.split(' ') - # uniform sampler2D myname; - # uniform layout(RGBA8) image3D myname; + # Examples: + # uniform sampler2D myname; + # uniform layout(RGBA8) image3D myname; if s[1].startswith('layout'): ctype = s[2] cid = s[3] @@ -144,57 +159,27 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): if cid[-1] == ';': cid = cid[:-1] - found = False # Unique check + found = False # Uniqueness check if ctype.startswith('sampler') or ctype.startswith('image') or ctype.startswith('uimage'): # Texture unit - for tu in con['texture_units']: # Texture already present + for tu in con['texture_units']: if tu['name'] == cid: + # Texture already present found = True break - if found == False: + if not found: if cid[-1] == ']': # Array of samplers - sampler2D mySamplers[2] # Add individual units - mySamplers[0], mySamplers[1] for i in range(int(cid[-2])): - tu = {} + tu = {'name': cid[:-2] + str(i) + ']'} con['texture_units'].append(tu) - tu['name'] = cid[:-2] + str(i) + ']' else: - tu = {} + tu = {'name': cid} con['texture_units'].append(tu) - tu['name'] = cid if ctype.startswith('image') or ctype.startswith('uimage'): tu['is_image'] = True - # Check for link - for l in c['links']: - if l['name'] == cid: - valid_link = True - if 'ifdef' in l: - def_found = False - for d in defs: - for link_def in l['ifdef']: - if d == link_def: - def_found = True - break - if def_found: - break - if not def_found: - valid_link = False + check_link(c, defs, cid, tu) - if 'ifndef' in l: - def_found = False - for d in defs: - for link_def in l['ifndef']: - if d == link_def: - def_found = True - break - if def_found: - break - if def_found: - valid_link = False - - if valid_link: - tu['link'] = l['link'] - break else: # Constant if cid.find('[') != -1: # Float arrays cid = cid.split('[')[0] @@ -203,49 +188,65 @@ def parse_shader(sres, c, con, defs, lines, parse_attributes): if const['name'] == cid: found = True break - if found == False: - const = {} + if not found: + const = { + 'type': ctype, + 'name': cid + } con['constants'].append(const) - const['type'] = ctype - const['name'] = cid - # Check for link - for l in c['links']: - if l['name'] == cid: - valid_link = True - if 'ifdef' in l: - def_found = False - for d in defs: - for link_def in l['ifdef']: - if d == link_def: - def_found = True - break - if def_found: - break - if not def_found: - valid_link = False + check_link(c, defs, cid, const) - if 'ifndef' in l: - def_found = False - for d in defs: - for link_def in l['ifndef']: - if d == link_def: - def_found = True - break - if def_found: - break - if def_found: - valid_link = False - if valid_link: - const['link'] = l['link'] +def check_link(source_context: dict, defs: list[str], cid: str, out: dict): + """Checks whether the uniform/constant with the given name (`cid`) + has a link stated in the json (`source_context`) that can be safely + included based on the given defines (`defs`). If that is the case, + the found link is written to the `out` dictionary. + """ + for link in source_context['links']: + if link['name'] == cid: + valid_link = True + + # Optionally only use link if at least + # one of the given defines is set + if 'ifdef' in link: + def_found = False + for d in defs: + for link_def in link['ifdef']: + if d == link_def: + def_found = True break + if def_found: + break + if not def_found: + valid_link = False -def make(res, base_name, json_data, fp, defs, make_variants): - sres = {} + # Optionally only use link if none of + # the given defines are set + if 'ifndef' in link: + def_found = False + for d in defs: + for link_def in link['ifndef']: + if d == link_def: + def_found = True + break + if def_found: + break + if def_found: + valid_link = False + + if valid_link: + out['link'] = link['link'] + break + + +def make(res: dict, base_name: str, json_data: dict, fp, defs: list[str], make_variants: bool): + sres = { + 'name': base_name, + 'contexts': [] + } res['shader_datas'].append(sres) - sres['name'] = base_name - sres['contexts'] = [] asset = assets.shader_passes_assets[base_name] vert = None