| # Copyright (C) Microsoft Corporation. All rights reserved. |
| # This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. |
| """hcttest-system-values.py - Test all system values with each signature point through fxc. |
| Builds csv tables for the results for each shader model from 4.0 through 5.1. |
| """ |
| import re |
| |
| SignaturePoints = [ |
| # sig point, stage, tessfactors already present |
| ('VSIn', 'vs', False), |
| ('VSOut', 'vs', False), |
| ('PCIn', 'hs', False), |
| ('HSIn', 'hs', False), |
| ('HSCPIn', 'hs', False), |
| ('HSCPOut', 'hs', False), |
| ('PCOut', 'hs', True ), |
| ('DSIn', 'ds', True ), |
| ('DSCPIn', 'ds', False), |
| ('DSOut', 'ds', False), |
| ('GSVIn', 'gs', False), |
| ('GSIn', 'gs', False), |
| ('GSOut', 'gs', False), |
| ('PSIn', 'ps', False), |
| ('PSOut', 'ps', False), |
| ('CSIn', 'cs', False), |
| ] |
| |
| SysValues = """ |
| VertexID |
| InstanceID |
| Position |
| RenderTargetArrayIndex |
| ViewportArrayIndex |
| ClipDistance |
| CullDistance |
| OutputControlPointID |
| DomainLocation |
| PrimitiveID |
| GSInstanceID |
| SampleIndex |
| IsFrontFace |
| Coverage |
| InnerCoverage |
| Target |
| Depth |
| DepthLessEqual |
| DepthGreaterEqual |
| StencilRef |
| DispatchThreadID |
| GroupID |
| GroupIndex |
| GroupThreadID |
| TessFactor |
| InsideTessFactor |
| """.split() |
| |
| def run(cmd, output_filename=None): |
| import os |
| print(cmd) |
| if output_filename: |
| if os.path.isfile(output_filename): |
| os.unlink(output_filename) |
| with file(output_filename, 'wt') as f: |
| f.write('%s\n\n' % cmd) |
| ret = os.system(cmd + ' >> "%s" 2>&1' % output_filename) |
| output = '' |
| if os.path.isfile(output_filename): |
| with open(output_filename, 'rt') as f: |
| output = f.read() |
| return ret, output |
| else: |
| ret = os.system(cmd) |
| return ret, None |
| |
| sig_examples = """ |
| // Patch Constant signature: |
| // |
| // Name Index Mask Register SysValue Format Used |
| // -------------------- ----- ------ -------- -------- ------- ------ |
| // Arb 0 x 0 NONE float x |
| // SV_TessFactor 0 x 1 QUADEDGE float x |
| // SV_TessFactor 1 x 2 QUADEDGE float x |
| // SV_TessFactor 2 x 3 QUADEDGE float x |
| // SV_TessFactor 3 x 4 QUADEDGE float x |
| // SV_InsideTessFactor 0 x 5 QUADINT float x |
| // SV_InsideTessFactor 1 x 6 QUADINT float x |
| // |
| // |
| // Input signature: |
| // |
| // Name Index Mask Register SysValue Format Used |
| // -------------------- ----- ------ -------- -------- ------- ------ |
| // Arb 0 x 0 NONE float |
| // |
| // |
| // Output signature: |
| // |
| // Name Index Mask Register SysValue Format Used |
| // -------------------- ----- ------ -------- -------- ------- ------ |
| // Arb 0 x 0 NONE float x |
| """ |
| |
| rxSigBegin = re.compile(r'^// (Input|Output|Patch Constant) signature:\s*$') |
| rxSigElementsBegin = re.compile(r'^// -------------------- ----- ------ -------- -------- ------- ------\s*$') |
| |
| # // SV_Target 1 xyzw 1 TARGET float xyzw |
| rxSigElement = re.compile(r'^// ([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*([^\s]+)?\s*$') |
| # Name, Index, Mask, Register, SysValue, Format, Used = m.groups() |
| |
| # For now, ParseSigs just looks for a matching semantic name and reports whether it uses |
| # a separate register (unpacked) and whether it's treated as arbitrary. |
| def ParseSigs(text, semantic): |
| "return separate_reg, as_arb for matching semantic" |
| it = iter(text.splitlines()) |
| sigtype = None |
| try: |
| while True: |
| line = it.next() |
| m = rxSigBegin.match(line) |
| if m: |
| sigtype = m.group(1) |
| continue |
| m = rxSigElementsBegin.match(line) |
| if m: |
| while True: |
| line = it.next() |
| m = rxSigElement.match(line) |
| if m: |
| Name, Index, Mask, Register, SysValue, Format, Used = m.groups() |
| if Name.lower() == semantic.lower(): |
| try: |
| regnum = int(Register) |
| reg = False |
| except: |
| reg = True |
| arb = SysValue == 'NONE' |
| return reg, arb |
| except StopIteration: |
| pass |
| return None |
| |
| # Internal error or validation error: |
| rxInternalError = re.compile(r"^(internal error:.*|error X8000:.*)$") |
| |
| # error X4502: invalid ps input semantic 'Foo' |
| # error X4502: SV_Coverage input not supported on ps_4_0 |
| # error X4502: SV_SampleIndex isn't supported on ps_4_0 |
| # error X3514: SV_GSInstanceID is an invalid input semantic for geometry shader primitives, it must be its own parameter. |
| # error X3514: 'GSMain': input parameter 'tfactor' must have a geometry specifier |
| # also errors for unsupported shader models |
| # error X3660: cs_4_0 does not support interlocked operations |
| rxSemanticErrors = [ |
| re.compile(r".*?\.hlsl.*?: error X4502: invalid .*? semantic '(\w+)'"), |
| re.compile(r".*?\.hlsl.*?: error X4502: (\w+) .*? supported on \w+"), |
| re.compile(r".*?\.hlsl.*?: error X3514: (\w+) is an invalid input semantic for geometry shader primitives, it must be its own parameter\."), |
| re.compile(r".*?\.hlsl.*?: error X3514: 'GSMain': input parameter '\w+' must have a geometry specifier"), |
| ] |
| def map_gen(fn, *sequences): |
| "generator style map" |
| iters = map(iter, sequences) |
| while True: |
| yield fn(*map(next, iters)) |
| def firstTrue(iterable): |
| "returns first non-False element, or None." |
| for it in iterable: |
| if it: |
| return it |
| |
| def ParseSVError(text, semantic): |
| "return true if error is about illegal use of matching semantic" |
| for line in text.splitlines(): |
| m = firstTrue(map_gen(lambda rx: rx.match(line), rxSemanticErrors)) |
| if m: |
| if len(m.groups()) < 1 or m.group(1).lower() == semantic.lower(): |
| return True |
| else: |
| m = rxInternalError.match(line) |
| if m: |
| print('#### Internal error detected!') |
| print(m.group(1)) |
| return 'InternalError' |
| return False |
| |
| # TODO: Fill in the correct error pattern |
| # error X4576: Non system-generated input signature parameter (Arb) cannot appear after a system generated value. |
| rxMustBeLastError = re.compile(r".*?\.hlsl.*?: error X4576: Non system-generated input signature parameter \(\w+\) cannot appear after a system generated value.") |
| def ParseSGVError(text, semantic): |
| "return true if error is about matching semantic having to be declared last" |
| for line in text.splitlines(): |
| m = rxMustBeLastError.match(line) |
| if m: |
| return True |
| else: |
| m = rxInternalError.match(line) |
| if m: |
| print('#### Internal error detected!') |
| print(m.group(1)) |
| return 'InternalError' |
| return False |
| |
| hlsl_filename = os.abspath(os.path.join() |
| os.environ['HLSL_SRC_DIR'], |
| r'tools\clang\test\HLSL', |
| 'system-values.hlsl')) |
| |
| def main(): |
| do('5_1') |
| do('5_0') |
| do('4_1') |
| do('4_0') |
| |
| def do(sm): |
| import os, sys |
| # set up table: |
| table = [[None] * len(SignaturePoints) for sv in SysValues] |
| null_filename = 'output\\test_sv_null.txt' |
| |
| for col, (sigpoint, stage, tfpresent) in enumerate(SignaturePoints): |
| entry = stage.upper() + 'Main' |
| target = stage + '_%s' % sm |
| |
| # test arb support: |
| ret, output = run('fxc %s /E %s /T %s /D%s_Defs=Def_Arb(float,Arb1,Arb1)' % |
| (hlsl_filename, entry, target, sigpoint), |
| null_filename) |
| arb_supported = ret == 0 |
| |
| # iterate all system values |
| sysvalues = tfpresent and SysValues[:-2] or SysValues |
| for row, sv in enumerate(sysvalues): |
| output_filename = 'output\\test_sv_output_%s_%s_%s.txt' % (sm, sv, sigpoint) |
| separate_reg, as_arb, def_last, result = False, False, False, 'NotInSig' |
| |
| ret, output = run('fxc %s /E %s /T %s /D%s_Defs=Def_%s' % |
| (hlsl_filename, entry, target, sigpoint, sv), |
| output_filename) |
| if ret: |
| # Failed, look for expected error message: |
| found = ParseSVError(output, 'SV_'+sv) |
| if found == 'InternalError': |
| table[row][col] = 'InternalError' |
| print('#### Internal error from ParseSVError - see "%s"' % output_filename) |
| elif not found: |
| table[row][col] = 'ParseSVError' |
| print('#### Error from ParseSVError - see "%s"' % output_filename) |
| else: |
| table[row][col] = 'NA' |
| if os.path.isfile(output_filename): |
| os.unlink(output_filename) |
| continue |
| |
| parse_result = ParseSigs(output, 'SV_'+sv) |
| if parse_result: |
| separate_reg, as_arb = parse_result |
| if as_arb: |
| if separate_reg: |
| table[row][col] = 'Error: both as_arb and separate_reg set!' |
| print('#### Error from ParseSigs, both as_arb and separate_reg set - see "%s"' % output_filename) |
| continue |
| result = 'Arb' |
| else: |
| if separate_reg: |
| result = 'NotPacked' |
| else: |
| result = 'SV' |
| if os.path.isfile(output_filename): |
| os.unlink(output_filename) |
| else: |
| print('## Not in signature? See "%s"' % output_filename) |
| |
| if arb_supported and not as_arb and not separate_reg: |
| output_filename = 'output\\test_sv_output_last_%s_%s_%s.txt' % (sm, sv, sigpoint) |
| # must system value be declared last? test by adding arb last if arb support |
| ret, output = run('fxc %s /E %s /T %s /D%s_Defs="Def_%s Def_Arb(float,Arb1,Arb1)"' % |
| (hlsl_filename, entry, target, sigpoint, sv), |
| output_filename) |
| if ret: |
| found = ParseSGVError(output, 'SV_'+sv) |
| if found == 'InternalError': |
| result += ' | InternalError found with ParseSGVError' |
| print('#### Internal error from ParseSGVError - see "%s"' % output_filename) |
| elif not found: |
| result += ' | ParseSGVError' |
| print('#### Error from ParseSGVError - see "%s"' % output_filename) |
| elif result == 'SV': |
| result = 'SGV' |
| if os.path.isfile(output_filename): |
| os.unlink(output_filename) |
| else: |
| result += ' | Error: last required detected, but not SV?' |
| print('#### Error: last required detected, but not SV? - see "%s"' % output_filename) |
| else: |
| if os.path.isfile(output_filename): |
| os.unlink(output_filename) |
| |
| table[row][col] = result |
| |
| for row in range(row+1, len(SysValues)): |
| table[row][col] = 'TessFactor' |
| |
| def WriteTable(writefn, table): |
| writefn('Semantic,' + ','.join([sigpoint for sigpoint, stage, tfpresent in SignaturePoints]) + '\n') |
| for n, row in enumerate(table): |
| writefn((SysValues[n]) + ','.join(row) + '\n') |
| |
| WriteTable(sys.stdout.write, table) |
| with open('fxc_sig_packing_table_%s.csv' % sm, 'wt') as f: |
| WriteTable(f.write, table) |
| |
| if __name__ == '__main__': |
| main() |