summaryrefslogtreecommitdiffstats
path: root/ui/surface/compile_hlsl.py
diff options
context:
space:
mode:
authornick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-14 03:42:45 +0000
committernick@chromium.org <nick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-14 03:42:45 +0000
commit264e10226c87d162b2015586a7e23fe3a95d84ca (patch)
tree7bb6a772ba9fd3ed31c375b199414a9b29cc3ca6 /ui/surface/compile_hlsl.py
parent95d7703d24aa95d0b68bbd5b8708edfa0e44bda0 (diff)
downloadchromium_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.py158
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)