diff options
author | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 03:42:45 +0000 |
---|---|---|
committer | nick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-14 03:42:45 +0000 |
commit | 264e10226c87d162b2015586a7e23fe3a95d84ca (patch) | |
tree | 7bb6a772ba9fd3ed31c375b199414a9b29cc3ca6 /ui/surface/compile_hlsl.py | |
parent | 95d7703d24aa95d0b68bbd5b8708edfa0e44bda0 (diff) | |
download | chromium_src-264e10226c87d162b2015586a7e23fe3a95d84ca.zip chromium_src-264e10226c87d162b2015586a7e23fe3a95d84ca.tar.gz chromium_src-264e10226c87d162b2015586a7e23fe3a95d84ca.tar.bz2 |
Teach GYP how to compile HLSL.
This is limited to the ui/surface directory for now, but
the GYP rule and python script are generalizable if we
need to do this elsewhere in the code in the future.
BUG=164714
TEST=unit tests
Review URL: https://chromiumcodereview.appspot.com/11484003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173070 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/surface/compile_hlsl.py')
-rw-r--r-- | ui/surface/compile_hlsl.py | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/ui/surface/compile_hlsl.py b/ui/surface/compile_hlsl.py new file mode 100644 index 0000000..eaa5794 --- /dev/null +++ b/ui/surface/compile_hlsl.py @@ -0,0 +1,158 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import optparse +import os.path +import re +import subprocess +import sys + + +def ConvertToCamelCase(input): + """Converts the input string from 'unix_hacker' style to 'CamelCase' style.""" + return ''.join(x[:1].upper() + x[1:] for x in input.split('_')) + + +def ExtractShaderTargetNamesFromSource(source_hlsl_file): + """Parses '@gyp_compile' and '@gyp_namespace' metadata from an .hlsl file.""" + # matches strings like // @gyp_compile(arg_a, arg_b) ... + gyp_compile = re.compile( + '^//\s*@gyp_compile\(\s*(?P<profile>[a-zA-Z0-9_]+)\s*,' + '\s*(?P<function_name>[a-zA-Z0-9_]+)\s*\).*') + # matches strings like // @gyp_namespace(arg_a) ... + gyp_namespace = re.compile( + '^//\s*@gyp_namespace\(\s*(?P<namespace>[a-zA-Z0-9_]+)\s*\).*') + + shader_targets = [] # tuples like ('vs_2_0', 'vertexMain') + namespace = None + with open(source_hlsl_file) as hlsl: + for line_number, line in enumerate(hlsl.read().splitlines(), 1): + m = gyp_compile.match(line) + if m: + shader_targets.append((m.group('profile'), m.group('function_name'))) + continue + m = gyp_namespace.match(line) + if m: + namespace = m.group('namespace') + continue + if '@gyp' in line: + print '%s(%d) : warning: ignoring malformed @gyp directive ' % ( + source_hlsl_file, line_number) + + if not shader_targets: + print ( +"""%s(%d) : error: Reached end of file without finding @gyp_compile directive. + + By convention, each HLSL source must contain one or more @gyp_compile + directives in its comments, as metadata informing the Chrome build tool + which entry points should be compiled. For example, to specify compilation + of a function named 'vertexMain' as a shader model 2 vertex shader: + + // @gyp_compile(vs_2_0, vertexMain) + + Or to compile a pixel shader 2.0 function named 'someOtherShader': + + // @gyp_compile(ps_2_0, someOtherShader) + + To wrap everything in a C++ namespace 'foo_bar', add a line somewhere like: + + // @gyp_namespace(foo_bar) + + (Namespaces are optional) +""" % (source_hlsl_file, line_number)) + sys.exit(1) + return (shader_targets, namespace) + + +def GetCppVariableName(function_name): + return 'k%s' % ConvertToCamelCase(function_name) + + +def CompileMultipleHLSLShadersToOneHeaderFile(fxc_compiler_path, + source_hlsl_file, + namespace, + shader_targets, + target_header_file, + target_cc_file): + """Compiles specified shaders from an .hlsl file into a single C++ header.""" + header_output = [] + # Invoke the compiler one at a time to write the c++ header file, + # then read that header file into |header_output|. + for (compiler_profile, hlsl_function_name) in shader_targets: + file_name_only = os.path.basename(source_hlsl_file) + base_filename, _ = os.path.splitext(file_name_only) + cpp_global_var_name = GetCppVariableName(hlsl_function_name) + + command = [fxc_compiler_path, + source_hlsl_file, # From this HLSL file + '/E', hlsl_function_name, # Compile one function + '/T', compiler_profile, # As a vertex or pixel shader + '/Vn', cpp_global_var_name, # Into a C++ constant thus named + '/Fh', target_header_file, # Declared in this C++ header file. + '/O3'] # Fast is better than slow. + (out, err) = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=False).communicate() + if err: + print 'Error while compiling %s in file %s' % ( + hlsl_function_name, source_hlsl_file) + print err + sys.exit(1) + with open(target_header_file, 'r') as header: + header_output.append(header.read()) + + # Now, re-write the .h and .cc files with the concatenation of all + # the individual passes. + classname = '%sHLSL' % (ConvertToCamelCase(base_filename)) + preamble = '\n'.join([ + '/' * 77, + '// This file is auto-generated from %s' % file_name_only, + '//', + "// To edit it directly would be a fool's errand.", + '/' * 77, + '', + '']) + with open(target_header_file, 'wb') as h: + h.write(preamble) + h.write('#pragma once\n') + h.write('#include <windows.h>\n\n') + if namespace: + h.write('namespace %s {\n\n' % namespace) + h.write('namespace %s {\n\n' % classname) + for _, function_name in shader_targets: + h.write('extern const BYTE %s[];\n' % GetCppVariableName(function_name)) + h.write('\n} // namespace %s\n' % classname) + if namespace: + h.write('\n} // namespace %s\n' % namespace) + + with open(target_cc_file, 'wb') as cc: + cc.write(preamble) + cc.write('#include "%s"\n\n' % os.path.basename(target_header_file)) + if namespace: + cc.write('namespace %s {\n\n' % namespace) + cc.write('namespace %s {\n\n' % classname) + cc.write(''.join(header_output)) + cc.write('\n} // namespace %s\n' % classname) + if namespace: + cc.write('\n} // namespace %s\n' % namespace) + + +if __name__ == '__main__': + parser = optparse.OptionParser() + parser.add_option('--shader_compiler_tool', dest='compiler') + parser.add_option('--output_h_file', dest='header_file') + parser.add_option('--output_cc_file', dest='cc_file') + parser.add_option('--input_hlsl_file', dest='hlsl_file') + (options, args) = parser.parse_args() + + hlsl_file = os.path.abspath(options.hlsl_file) + shader_targets, namespace = ExtractShaderTargetNamesFromSource(hlsl_file) + + CompileMultipleHLSLShadersToOneHeaderFile(options.compiler, + hlsl_file, + namespace, + shader_targets, + options.header_file, + options.cc_file) |