gecko-dev/gfx/layers/d3d11/genshaders.py

172 строки
4.8 KiB
Python
Executable File

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import argparse
import codecs
import locale
import os
import re
import subprocess
import sys
import tempfile
import yaml
import buildconfig
def shell_main():
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output', type=str, required=True,
help='Output file')
parser.add_argument('manifest', type=str,
help='Manifest source file')
args = parser.parse_args()
with open(args.output, 'w') as out_file:
process_manifest(out_file, args.manifest)
def main(output_fp, input_filename):
return process_manifest(output_fp, input_filename)
HEADER = """// AUTOGENERATED - DO NOT EDIT
namespace mozilla {
namespace layers {
struct ShaderBytes { const void* mData; size_t mLength; };
"""
FOOTER = """
} // namespace layers
} // namespace mozilla"""
def process_manifest(output_fp, manifest_filename):
with codecs.open(manifest_filename, 'r', 'UTF-8') as in_fp:
manifest = yaml.safe_load(in_fp)
shader_folder, _ = os.path.split(manifest_filename)
output_fp.write(HEADER)
deps = set()
for block in manifest:
if 'type' not in block:
raise Exception("Expected 'type' key with shader mode")
if 'file' not in block:
raise Exception("Expected 'file' key with shader file")
if 'shaders' not in block:
raise Exception("Expected 'shaders' key with shader name list")
shader_file = os.path.join(shader_folder, block['file'])
deps.add(shader_file)
shader_model = block['type']
for shader_name in block['shaders']:
new_deps = run_fxc(
shader_model=shader_model,
shader_file=shader_file,
shader_name=shader_name,
output_fp=output_fp)
deps |= new_deps
output_fp.write(FOOTER)
return deps
def run_fxc(shader_model,
shader_file,
shader_name,
output_fp):
fxc_location = buildconfig.substs['FXC']
argv = [
fxc_location,
'-nologo',
'-T{0}'.format(shader_model),
shader_file,
'-E{0}'.format(shader_name),
'-Vn{0}'.format(shader_name),
'-Vi',
]
if 'Linux' in buildconfig.substs['HOST_OS_ARCH']:
argv.insert(0, buildconfig.substs['WINE'])
if shader_model.startswith('vs_'):
argv += ['-DVERTEX_SHADER']
elif shader_model.startswith('ps_'):
argv += ['-DPIXEL_SHADER']
deps = None
with ScopedTempFilename() as temp_filename:
argv += ['-Fh{0}'.format(temp_filename)]
sys.stdout.write('{0}\n'.format(' '.join(argv)))
proc_stdout = subprocess.check_output(argv)
proc_stdout = decode_console_text(sys.stdout, proc_stdout)
deps = find_dependencies(proc_stdout)
assert 'fxc2' in fxc_location or len(deps) > 0
with open(temp_filename, 'r') as temp_fp:
output_fp.write(temp_fp.read())
output_fp.write("ShaderBytes s{0} = {{ {0}, sizeof({0}) }};\n".format(
shader_name))
return deps
def find_dependencies(fxc_output):
# Dependencies look like this:
# Resolved to [<path>]
#
# Microsoft likes to change output strings based on the user's language, so
# instead of pattern matching on that string, we take everything in between
# brackets. We filter out potentially bogus strings later.
deps = set()
for line in fxc_output.split('\n'):
m = re.search(r"\[([^\]]+)\]", line)
if m is None:
continue
dep_path = m.group(1)
dep_path = os.path.normpath(dep_path)
if os.path.isfile(dep_path):
deps.add(dep_path)
return deps
# Python reads the raw bytes from stdout, so we need to try our best to
# capture that as a valid Python string.
def decode_console_text(pipe, text):
try:
if pipe.encoding:
return text.decode(pipe.encoding, 'replace')
except Exception:
pass
try:
return text.decode(locale.getpreferredencoding(), 'replace')
except Exception:
return text.decode('utf8', 'replace')
# Allocate a temporary file name and delete it when done. We need an extra
# wrapper for this since TemporaryNamedFile holds the file open.
class ScopedTempFilename(object):
def __init__(self):
self.name = None
def __enter__(self):
with tempfile.NamedTemporaryFile(delete=False) as tmp:
self.name = tmp.name
return self.name
def __exit__(self, type, value, traceback):
if not self.name:
return
try:
os.unlink(self.name)
except Exception:
pass
if __name__ == '__main__':
shell_main()