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.
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')
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
)
33 shader_targets
.append((m
.group('profile'), m
.group('function_name')))
35 m
= gyp_namespace
.match(line
)
37 namespace
= m
.group('namespace')
40 print '%s(%d) : warning: ignoring malformed @gyp directive ' % (
41 source_hlsl_file
, line_number
)
43 if not shader_targets
:
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
))
65 return (shader_targets
, namespace
)
68 def GetCppVariableName(function_name
):
69 return 'k%s' % ConvertToCamelCase(function_name
)
72 def CompileMultipleHLSLShadersToOneHeaderFile(fxc_compiler_path
,
78 """Compiles specified shaders from an .hlsl file into a single C++ header."""
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
,
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
)
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([
112 '// This file is auto-generated from %s' % file_name_only
,
114 "// To edit it directly would be a fool's errand.",
118 with
open(target_header_file
, 'wb') as h
:
120 h
.write('#pragma once\n')
121 h
.write('#include <windows.h>\n\n')
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
)
129 h
.write('\n} // namespace %s\n' % namespace
)
131 with
open(target_cc_file
, 'wb') as cc
:
133 cc
.write('#include "%s"\n\n' % os
.path
.basename(target_header_file
))
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
)
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
,