The browser-side WebSocket implementation needs to avoid sending invalid
[chromium-blink-merge.git] / ui / surface / compile_hlsl.py
blob3584804a1b2c31baf79956c56a9e51bf61a2b4ee
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 import optparse
6 import os.path
7 import re
8 import subprocess
9 import sys
12 def ConvertToCamelCase(input):
13 """Converts the input string from 'unix_hacker' style to 'CamelCase' style."""
14 return ''.join(x[:1].upper() + x[1:] for x in input.split('_'))
17 def ExtractShaderTargetNamesFromSource(source_hlsl_file):
18 """Parses '@gyp_compile' and '@gyp_namespace' metadata from an .hlsl file."""
19 # matches strings like // @gyp_compile(arg_a, arg_b) ...
20 gyp_compile = re.compile(
21 '^//\s*@gyp_compile\(\s*(?P<profile>[a-zA-Z0-9_]+)\s*,'
22 '\s*(?P<function_name>[a-zA-Z0-9_]+)\s*\).*')
23 # matches strings like // @gyp_namespace(arg_a) ...
24 gyp_namespace = re.compile(
25 '^//\s*@gyp_namespace\(\s*(?P<namespace>[a-zA-Z0-9_]+)\s*\).*')
27 shader_targets = [] # tuples like ('vs_2_0', 'vertexMain')
28 namespace = None
29 with open(source_hlsl_file) as hlsl:
30 for line_number, line in enumerate(hlsl.read().splitlines(), 1):
31 m = gyp_compile.match(line)
32 if m:
33 shader_targets.append((m.group('profile'), m.group('function_name')))
34 continue
35 m = gyp_namespace.match(line)
36 if m:
37 namespace = m.group('namespace')
38 continue
39 if '@gyp' in line:
40 print '%s(%d) : warning: ignoring malformed @gyp directive ' % (
41 source_hlsl_file, line_number)
43 if not shader_targets:
44 print (
45 """%s(%d) : error: Reached end of file without finding @gyp_compile directive.
47 By convention, each HLSL source must contain one or more @gyp_compile
48 directives in its comments, as metadata informing the Chrome build tool
49 which entry points should be compiled. For example, to specify compilation
50 of a function named 'vertexMain' as a shader model 2 vertex shader:
52 // @gyp_compile(vs_2_0, vertexMain)
54 Or to compile a pixel shader 2.0 function named 'someOtherShader':
56 // @gyp_compile(ps_2_0, someOtherShader)
58 To wrap everything in a C++ namespace 'foo_bar', add a line somewhere like:
60 // @gyp_namespace(foo_bar)
62 (Namespaces are optional)
63 """ % (source_hlsl_file, line_number))
64 sys.exit(1)
65 return (shader_targets, namespace)
68 def GetCppVariableName(function_name):
69 return 'k%s' % ConvertToCamelCase(function_name)
72 def CompileMultipleHLSLShadersToOneHeaderFile(fxc_compiler_path,
73 source_hlsl_file,
74 namespace,
75 shader_targets,
76 target_header_file,
77 target_cc_file):
78 """Compiles specified shaders from an .hlsl file into a single C++ header."""
79 header_output = []
80 # Invoke the compiler one at a time to write the c++ header file,
81 # then read that header file into |header_output|.
82 for (compiler_profile, hlsl_function_name) in shader_targets:
83 file_name_only = os.path.basename(source_hlsl_file)
84 base_filename, _ = os.path.splitext(file_name_only)
85 cpp_global_var_name = GetCppVariableName(hlsl_function_name)
87 command = [fxc_compiler_path,
88 source_hlsl_file, # From this HLSL file
89 '/E', hlsl_function_name, # Compile one function
90 '/T', compiler_profile, # As a vertex or pixel shader
91 '/Vn', cpp_global_var_name, # Into a C++ constant thus named
92 '/Fh', target_header_file, # Declared in this C++ header file.
93 '/O3'] # Fast is better than slow.
94 child = subprocess.Popen(command,
95 stdout=subprocess.PIPE,
96 stderr=subprocess.PIPE,
97 shell=False)
98 (out, err) = child.communicate()
99 if err or child.returncode:
100 print 'Error (%d) while compiling %s in file %s' % (
101 child.returncode, hlsl_function_name, source_hlsl_file)
102 print err
103 sys.exit(1)
104 with open(target_header_file, 'r') as header:
105 header_output.append(header.read())
107 # Now, re-write the .h and .cc files with the concatenation of all
108 # the individual passes.
109 classname = '%sHLSL' % (ConvertToCamelCase(base_filename))
110 preamble = '\n'.join([
111 '/' * 77,
112 '// This file is auto-generated from %s' % file_name_only,
113 '//',
114 "// To edit it directly would be a fool's errand.",
115 '/' * 77,
117 ''])
118 with open(target_header_file, 'wb') as h:
119 h.write(preamble)
120 h.write('#pragma once\n')
121 h.write('#include <windows.h>\n\n')
122 if namespace:
123 h.write('namespace %s {\n\n' % namespace)
124 h.write('namespace %s {\n\n' % classname)
125 for _, function_name in shader_targets:
126 h.write('extern const BYTE %s[];\n' % GetCppVariableName(function_name))
127 h.write('\n} // namespace %s\n' % classname)
128 if namespace:
129 h.write('\n} // namespace %s\n' % namespace)
131 with open(target_cc_file, 'wb') as cc:
132 cc.write(preamble)
133 cc.write('#include "%s"\n\n' % os.path.basename(target_header_file))
134 if namespace:
135 cc.write('namespace %s {\n\n' % namespace)
136 cc.write('namespace %s {\n\n' % classname)
137 cc.write(''.join(header_output))
138 cc.write('\n} // namespace %s\n' % classname)
139 if namespace:
140 cc.write('\n} // namespace %s\n' % namespace)
143 if __name__ == '__main__':
144 parser = optparse.OptionParser()
145 parser.add_option('--shader_compiler_tool', dest='compiler')
146 parser.add_option('--output_h_file', dest='header_file')
147 parser.add_option('--output_cc_file', dest='cc_file')
148 parser.add_option('--input_hlsl_file', dest='hlsl_file')
149 (options, args) = parser.parse_args()
151 hlsl_file = os.path.abspath(options.hlsl_file)
152 shader_targets, namespace = ExtractShaderTargetNamesFromSource(hlsl_file)
154 header_file = os.path.normpath(options.header_file)
155 cc_file = os.path.normpath(options.cc_file)
156 CompileMultipleHLSLShadersToOneHeaderFile(options.compiler,
157 hlsl_file,
158 namespace,
159 shader_targets,
160 header_file,
161 cc_file)