def generate(input, out_src, out_hdr, args): with open(input, 'r') as f: try: yml = yaml.load(f) except yaml.YAMLError as exc: # show a proper error if YAML parsing fails util.setErrorLocation(exc.problem_mark.name, exc.problem_mark.line - 1) util.fmtError('YAML error: {}'.format(exc.problem)) util.setErrorLocation(input, 1) src_root_path = os.path.dirname(input) + '/' dst_dir = args['deploy_dir'] + '/' if 'options' in yml: if 'src_dir' in yml['options']: src_root_path += yml['options']['src_dir'] + '/' if util.getEnv('target_platform') == 'ios' and 'ios' in yml['options']: if 'dst_dir' in yml['options']['ios']: dst_dir += yml['options']['ios']['dst_dir'] + '/' elif util.getEnv( 'target_platform') == 'osx' and 'macos' in yml['options']: if 'dst_dir' in yml['options']['macos']: dst_dir += yml['options']['macos']['dst_dir'] + '/' elif 'dst_dir' in yml['options']: dst_dir += yml['options']['dst_dir'] del yml['options'] if not os.path.exists(dst_dir): os.makedirs(dst_dir) if check_dirty(src_root_path, input, out_hdr, yml): copy_files(src_root_path, dst_dir, yml) gen_header(out_hdr, yml)
def parseOutput(output, lines) : ''' Parse error output lines from FXC, map them to the original source code location and output an error message compatible with Xcode or VStudio ''' hasError = False hasWarning = False outLines = output.splitlines() for outLine in outLines : # extract generated shader source column, line and message # format is 'filename(line,startcol-endcol): msg lineStartIndex = outLine.find('(', 0) + 1 if lineStartIndex == 0 : continue lineEndIndex = outLine.find(',', lineStartIndex) if lineEndIndex == -1 : continue colStartIndex = lineEndIndex + 1 colEndIndex = outLine.find('-', colStartIndex) if colEndIndex == -1 : colEndIndex = outLine.find(')', colStartIndex) if colEndIndex == -1 : continue msgStartIndex = outLine.find(':', colStartIndex+1) if msgStartIndex == -1 : continue colNr = int(outLine[colStartIndex:colEndIndex]) lineNr = int(outLine[lineStartIndex:lineEndIndex]) msg = outLine[msgStartIndex:] # map to original location lineIndex = lineNr - 1 if lineIndex >= len(lines) : lineIndex = len(lines) - 1 srcPath = lines[lineIndex].path srcLineNr = lines[lineIndex].lineNumber # and output... util.setErrorLocation(srcPath, srcLineNr) if 'error' in outLine : hasError = True util.fmtError(msg, False) elif 'warning' in outLine : hasWarning = True util.fmtWarning(msg) if hasError : for line in lines : print(line.content) sys.exit(10)
def parseSource(self, fileName): f = open(fileName, 'r') self.fileName = fileName self.lineNumber = 0 for line in f: util.setErrorLocation(self.fileName, self.lineNumber) self.parseLine(line) self.lineNumber += 1 f.close() if self.current is not None: util.fmtError('missing @end at end of file')
def parseOutput(output, lines): ''' Parse error output lines from FXC, map them to the original source code location and output an error message compatible with Xcode or VStudio ''' hasError = False hasWarning = False outLines = output.splitlines() for outLine in outLines: # extract generated shader source column, line and message # format is 'filename(line,startcol-endcol): msg lineStartIndex = outLine.find('(', 0) + 1 if lineStartIndex == 0: continue lineEndIndex = outLine.find(',', lineStartIndex) if lineEndIndex == -1: continue colStartIndex = lineEndIndex + 1 colEndIndex = outLine.find('-', colStartIndex) if colEndIndex == -1: colEndIndex = outLine.find(')', colStartIndex) if colEndIndex == -1: continue msgStartIndex = outLine.find(':', colStartIndex + 1) if msgStartIndex == -1: continue colNr = int(outLine[colStartIndex:colEndIndex]) lineNr = int(outLine[lineStartIndex:lineEndIndex]) msg = outLine[msgStartIndex:] # map to original location lineIndex = lineNr - 1 if lineIndex >= len(lines): lineIndex = len(lines) - 1 srcPath = lines[lineIndex].path srcLineNr = lines[lineIndex].lineNumber # and output... util.setErrorLocation(srcPath, srcLineNr) if 'error' in outLine: hasError = True util.fmtError(msg, False) elif 'warning' in outLine: hasWarning = True util.fmtWarning(msg) if hasError: for line in lines: print(line.content) sys.exit(10)
def resolveDeps(self, shd, dep): ''' Recursively resolve dependencies for a shader. ''' # just add new dependencies at the end of resolvedDeps, # and remove dups in a second pass after recursion if not dep.name in self.blocks: util.setErrorLocation(dep.path, dep.lineNumber) util.fmtError("unknown block dependency '{}'".format(dep.name)) shd.resolvedDeps.append(dep.name) for depdep in self.blocks[dep.name].dependencies: self.resolveDeps(shd, depdep)
def compile(input, base_path, slangs): util.setErrorLocation(input, 0) for slang in slangs: if 'glsl' in slang: src_slang = 'glsl' else: src_slang = slang src_path = '{}.{}.spv'.format(base_path, src_slang) dst_path = '{}.{}'.format(base_path, slang) tool = getToolPath() cmd = [tool, '-spirv', src_path, '-o', dst_path, '-lang', slang] run(cmd)
def resolveDeps(self, shd, dep) : ''' Recursively resolve dependencies for a shader. ''' # just add new dependencies at the end of resolvedDeps, # and remove dups in a second pass after recursion if not dep.name in self.blocks : util.setErrorLocation(dep.path, dep.lineNumber) util.fmtError("unknown block dependency '{}'".format(dep.name)) shd.resolvedDeps.append(dep.name) for depdep in self.blocks[dep.name].dependencies : self.resolveDeps(shd, depdep)
def checkAddUniform(self, uniform, list): ''' Check if uniform already exists in list, if yes check if type and binding matches, if not write error. If uniform doesn't exist yet in list, add it. ''' listUniform = findByName(uniform.name, list) if listUniform is not None: # redundant uniform, check if type and binding name match if listUniform.type != uniform.type: util.setErrorLocation(uniform.filePath, uniform.lineNumber) util.fmtError( "uniform type mismatch '{}' vs '{}'".format( uniform.type, listUniform.type), False) util.setErrorLocation(listUniform.filePath, listUniform.lineNumber) util.fmtError("uniform type mismatch '{}' vs '{}'".format( listUniform.type, uniform.type)) if listUniform.bind != uniform.bind: util.setErrorLocation(uniform.filePath, uniform.lineNumber) util.fmtError( "uniform bind name mismatch '{}' vs '{}'".format( uniform.bind, listUniform.bind), False) util.setErrorLocation(listUniform.filePath, listUniform.lineNumber) util.fmtError("uniform bind name mismatch '{}' vs '{}'".format( listUniform.bind, uniform.bind)) else: # new uniform from block, add to list list.append(uniform)
def resolveBundleUniforms(self, bundle) : ''' Gathers all uniforms from all shaders in the bundle. ''' for program in bundle.programs : if program.vs not in self.vertexShaders : util.setErrorLocation(program.filePath, program.lineNumber) util.fmtError("unknown vertex shader '{}'".format(program.vs)) for uniform in self.vertexShaders[program.vs].uniforms : self.checkAddUniform(uniform, bundle.uniforms) if program.fs not in self.fragmentShaders : util.setErrorLocation(program.filePath, program.lineNumber) util.fmtError("unknown fragment shader '{}'".format(program.fs)) for uniform in self.fragmentShaders[program.fs].uniforms : self.checkAddUniform(uniform, bundle.uniforms)
def parseSource(self, fileName) : ''' Parse a single file and populate shader lib ''' f = open(fileName, 'r') self.fileName = fileName self.lineNumber = 0 for line in f : util.setErrorLocation(self.fileName, self.lineNumber) self.parseLine(line) self.lineNumber += 1 f.close() # all blocks must be closed if self.current is not None : util.fmtError('missing @end at end of file')
def generateShaderSources(self): for shd in self.shaders: lines = [] for l in shd.lines: # @include statement? if l.include: if l.include not in self.blocks: util.setErrorLocation(incl.path, incl.lineNumber) util.fmtError( "included block '{}' doesn't exist".format( incl.name)) for lb in self.blocks[l.include].lines: lines.append(lb) else: lines.append(l) shd.generatedSource = lines
def parseSource(self, fileName): ''' Parse a single file and populate shader lib ''' f = open(fileName, 'r') self.fileName = fileName self.lineNumber = 0 for line in f: util.setErrorLocation(self.fileName, self.lineNumber) self.parseLine(line) self.lineNumber += 1 f.close() # all blocks must be closed if self.current is not None: util.fmtError('missing @end at end of file')
def generate(input, out_src, out_hdr, args): with open(input, 'r') as f: try: yml = yaml.load(f) except yaml.YAMLError as exc: # show a proper error if YAML parsing fails util.setErrorLocation(exc.problem_mark.name, exc.problem_mark.line-1) util.fmtError('YAML error: {}'.format(exc.problem)) src_root_path = os.path.dirname(input) + '/' if 'root' in yml: src_root_path += yml['root'] + '/' deploy_dir = args['deploy_dir'] + '/' if not os.path.exists(deploy_dir): os.makedirs(deploy_dir) if check_dirty(src_root_path, input, out_hdr, yml): copy_files(src_root_path, deploy_dir, yml) gen_header(out_hdr, yml)
def resolveBundleUniforms(self, bundle): ''' Gathers all uniforms from all shaders in the bundle. ''' for program in bundle.programs: if program.vs not in self.vertexShaders: util.setErrorLocation(program.filePath, program.lineNumber) util.fmtError("unknown vertex shader '{}'".format(program.vs)) for uniform in self.vertexShaders[program.vs].uniforms: self.checkAddUniform(uniform, bundle.uniforms) if program.fs not in self.fragmentShaders: util.setErrorLocation(program.filePath, program.lineNumber) util.fmtError("unknown fragment shader '{}'".format( program.fs)) for uniform in self.fragmentShaders[program.fs].uniforms: self.checkAddUniform(uniform, bundle.uniforms)
def parseOutput(output, lines) : ''' Parse error output lines from the GLSL reference compiler, map them to the original source code location and output an error message compatible with Xcode or VStudio ''' hasError = False outLines = output.splitlines() for outLine in outLines : if outLine.startswith('ERROR: ') : hasError = True # extract generated shader source column, line and message colStartIndex = 7 colEndIndex = outLine.find(':', colStartIndex) if colEndIndex == -1 : continue lineStartIndex = colEndIndex + 1 lineEndIndex = outLine.find(':', lineStartIndex) if lineEndIndex == -1 : continue msgStartIndex = lineEndIndex + 1 colNr = int(outLine[colStartIndex:colEndIndex]) lineNr = int(outLine[lineStartIndex:lineEndIndex]) msg = outLine[msgStartIndex:] # map to original location lineIndex = lineNr - 1 if lineIndex >= len(lines) : lineIndex = len(lines) - 1 srcPath = lines[lineIndex].path srcLineNr = lines[lineIndex].lineNumber # and output... util.setErrorLocation(srcPath, srcLineNr) util.fmtError(msg, False) if hasError : for line in lines : print "{}\n".format(line.content) util.fmtError("Aborting.")
def parseOutput(output, lines): hasError = False hasWarnings = False outLines = output.splitlines() for outLine in outLines: if 'error:' in outLine or 'warning:' in outLine or 'note:' in outLine: tokens = outLine.split(':') metalSrcPath = tokens[0] lineNr = int(tokens[1]) colNr = int(tokens[2]) msgType = tokens[3] msg = tokens[4] if msgType == ' error': hasError = True if msgType == ' warning': hasWarning = True # map to original location lineIndex = lineNr - 1 if lineIndex >= len(lines): lineIndex = len(lines) - 1 srcPath = lines[lineIndex].path srcLineNr = lines[lineIndex].lineNumber # and output... util.setErrorLocation(srcPath, srcLineNr) util.fmtError(msg, False) if hasError: for outLine in outLines: print(outLine) for line in lines: print(line.content) sys.exit(10)
def parseOutput(output, lines) : hasError = False hasWarnings = False outLines = output.splitlines() for outLine in outLines : if 'error:' in outLine or 'warning:' in outLine or 'note:' in outLine: tokens = outLine.split(':') metalSrcPath = tokens[0] lineNr = int(tokens[1]) colNr = int(tokens[2]) msgType = tokens[3] msg = tokens[4] if msgType == ' error': hasError = True if msgType == ' warning': hasWarning = True # map to original location lineIndex = lineNr - 1 if lineIndex >= len(lines) : lineIndex = len(lines) - 1 srcPath = lines[lineIndex].path srcLineNr = lines[lineIndex].lineNumber # and output... util.setErrorLocation(srcPath, srcLineNr) util.fmtError(msg, False) if hasError : for outLine in outLines : print(outLine) for line in lines : print(line.content) sys.exit(10)
def checkAddUniform(self, uniform, list) : ''' Check if uniform already exists in list, if yes check if type and binding matches, if not write error. If uniform doesn't exist yet in list, add it. ''' listUniform = findByName(uniform.name, list) if listUniform is not None: # redundant uniform, check if type and binding name match if listUniform.type != uniform.type : util.setErrorLocation(uniform.filePath, uniform.lineNumber) util.fmtError("uniform type mismatch '{}' vs '{}'".format(uniform.type, listUniform.type), False) util.setErrorLocation(listUniform.filePath, listUniform.lineNumber) util.fmtError("uniform type mismatch '{}' vs '{}'".format(listUniform.type, uniform.type)) if listUniform.bind != uniform.bind : util.setErrorLocation(uniform.filePath, uniform.lineNumber) util.fmtError("uniform bind name mismatch '{}' vs '{}'".format(uniform.bind, listUniform.bind), False) util.setErrorLocation(listUniform.filePath, listUniform.lineNumber) util.fmtError("uniform bind name mismatch '{}' vs '{}'".format(listUniform.bind, uniform.bind)) else : # new uniform from block, add to list list.append(uniform)
def validate(self, slangs): ''' Runs additional validation check after programs are resolved and before shader code is generated: - check whether each vs and fs is part of a program - check vertex shader inputs for valid types and names - check whether vertex shader output matches fragment shader input ''' for shd in self.shaders: for prog in self.programs.values(): prog_shd = prog.vs if shd.getTag() == 'vs' else prog.fs if shd.name == prog_shd: break else: util.setErrorLocation(shd.lines[0].path, shd.lines[0].lineNumber) util.fmtError( "vertex shader '{}' is not part of a program".format( shd.name), False) fatalError = True for slang in slangs: for vs in self.vertexShaders.values(): refl = vs.slReflection[slang] util.setErrorLocation(vs.lines[0].path, vs.lines[0].lineNumber) vs_inputs = refl['inputs'] for vs_input in vs_inputs: if vs_input['name'] not in validVsInNames: util.fmtError( "invalid vertex shader input name '{}', must be ({})" .format(vs_input['name'], ','.join(validVsInNames))) if vs_input['type'] not in validInOutTypes: util.fmtError( "invalid vertex shader input type '{}', must be ({})" .format(vs_input['type'], ','.join(validInOutTypes))) for ub in refl['uniform_blocks']: for m in ub['members']: validTypes = validUniformTypes if m[ 'num'] == 1 else validUniformArrayTypes if m['type'] not in validTypes: util.fmtError( "invalid uniform block member type '{}', must be ({})" .format(m['type'], ','.join(validTypes))) for fs in self.fragmentShaders.values(): refl = fs.slReflection[slang] util.setErrorLocation(fs.lines[0].path, fs.lines[0].lineNumber) for ub in refl['uniform_blocks']: for m in ub['members']: validTypes = validUniformTypes if m[ 'num'] == 1 else validUniformArrayTypes if m['type'] not in validTypes: util.fmtError( "invalid uniform block member type '{}', must be ({})" .format(m['type'], ','.join(validTypes))) for prog in self.programs.values(): vs = self.vertexShaders[prog.vs] fs = self.fragmentShaders[prog.fs] vs_outputs = vs.slReflection[slang]['outputs'] fs_inputs = fs.slReflection[slang]['inputs'] vs_fs_error = False if len(vs_outputs) == len(fs_inputs): for vs_out in vs_outputs: in_out_match = False for fs_in in fs_inputs: if (vs_out['name'] == fs_in['name']) and (vs_out['type'] == fs_in['type']): in_out_match = True break if not in_out_match: vs_fs_error = True if vs_fs_error: # number of inputs/outputs don't match vs_fs_error = True util.setErrorLocation(vs.lines[0].path, vs.lines[0].lineNumber) util.fmtError( "outputs of vs '{}' don't match inputs of fs '{}' (unused items might have been removed)" .format(vs.name, fs.name))