1// Copyright 2015 The Shaderc Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef SHADERC_SHADERC_HPP_
16#define SHADERC_SHADERC_HPP_
17
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "shaderc.h"
23
24namespace shaderc {
25// A CompilationResult contains the compiler output, compilation status,
26// and messages.
27//
28// The compiler output is stored as an array of elements and accessed
29// via random access iterators provided by cbegin() and cend().  The iterators
30// are contiguous in the sense of "Contiguous Iterators: A Refinement of
31// Random Access Iterators", Nevin Liber, C++ Library Evolution Working
32// Group Working Paper N3884.
33// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3884.pdf
34//
35// Methods begin() and end() are also provided to enable range-based for.
36// They are synonyms to cbegin() and cend(), respectively.
37template <typename OutputElementType>
38class CompilationResult {
39 public:
40  typedef OutputElementType element_type;
41  // The type used to describe the begin and end iterators on the
42  // compiler output.
43  typedef const OutputElementType* const_iterator;
44
45  // Upon creation, the CompilationResult takes ownership of the
46  // shaderc_compilation_result instance. During destruction of the
47  // CompilationResult, the shaderc_compilation_result will be released.
48  explicit CompilationResult(shaderc_compilation_result_t compilation_result)
49      : compilation_result_(compilation_result) {}
50  ~CompilationResult() { shaderc_result_release(compilation_result_); }
51
52  CompilationResult(CompilationResult&& other) {
53    compilation_result_ = other.compilation_result_;
54    other.compilation_result_ = nullptr;
55  }
56
57  // Returns any error message found during compilation.
58  std::string GetErrorMessage() const {
59    if (!compilation_result_) {
60      return "";
61    }
62    return shaderc_result_get_error_message(compilation_result_);
63  }
64
65  // Returns the compilation status, indicating whether the compilation
66  // succeeded, or failed due to some reasons, like invalid shader stage or
67  // compilation errors.
68  shaderc_compilation_status GetCompilationStatus() const {
69    if (!compilation_result_) {
70      return shaderc_compilation_status_null_result_object;
71    }
72    return shaderc_result_get_compilation_status(compilation_result_);
73  }
74
75  // Returns a random access (contiguous) iterator pointing to the start
76  // of the compilation output.  It is valid for the lifetime of this object.
77  // If there is no compilation result, then returns nullptr.
78  const_iterator cbegin() const {
79    if (!compilation_result_) return nullptr;
80    return reinterpret_cast<const_iterator>(
81        shaderc_result_get_bytes(compilation_result_));
82  }
83
84  // Returns a random access (contiguous) iterator pointing to the end of
85  // the compilation output.  It is valid for the lifetime of this object.
86  // If there is no compilation result, then returns nullptr.
87  const_iterator cend() const {
88    if (!compilation_result_) return nullptr;
89    return cbegin() +
90           shaderc_result_get_length(compilation_result_) /
91               sizeof(OutputElementType);
92  }
93
94  // Returns the same iterator as cbegin().
95  const_iterator begin() const { return cbegin(); }
96  // Returns the same iterator as cend().
97  const_iterator end() const { return cend(); }
98
99  // Returns the number of warnings generated during the compilation.
100  size_t GetNumWarnings() const {
101    if (!compilation_result_) {
102      return 0;
103    }
104    return shaderc_result_get_num_warnings(compilation_result_);
105  }
106
107  // Returns the number of errors generated during the compilation.
108  size_t GetNumErrors() const {
109    if (!compilation_result_) {
110      return 0;
111    }
112    return shaderc_result_get_num_errors(compilation_result_);
113  }
114
115 private:
116  CompilationResult(const CompilationResult& other) = delete;
117  CompilationResult& operator=(const CompilationResult& other) = delete;
118
119  shaderc_compilation_result_t compilation_result_;
120};
121
122// A compilation result for a SPIR-V binary module, which is an array
123// of uint32_t words.
124using SpvCompilationResult = CompilationResult<uint32_t>;
125// A compilation result in SPIR-V assembly syntax.
126using AssemblyCompilationResult = CompilationResult<char>;
127// Preprocessed source text.
128using PreprocessedSourceCompilationResult = CompilationResult<char>;
129
130// Contains any options that can have default values for a compilation.
131class CompileOptions {
132 public:
133  CompileOptions() { options_ = shaderc_compile_options_initialize(); }
134  ~CompileOptions() { shaderc_compile_options_release(options_); }
135  CompileOptions(const CompileOptions& other) {
136    options_ = shaderc_compile_options_clone(other.options_);
137  }
138  CompileOptions(CompileOptions&& other) {
139    options_ = other.options_;
140    other.options_ = nullptr;
141  }
142
143  // Adds a predefined macro to the compilation options. It behaves the same as
144  // shaderc_compile_options_add_macro_definition in shaderc.h.
145  void AddMacroDefinition(const char* name, size_t name_length,
146                          const char* value, size_t value_length) {
147    shaderc_compile_options_add_macro_definition(options_, name, name_length,
148                                                 value, value_length);
149  }
150
151  // Adds a valueless predefined macro to the compilation options.
152  void AddMacroDefinition(const std::string& name) {
153    AddMacroDefinition(name.c_str(), name.size(), nullptr, 0u);
154  }
155
156  // Adds a predefined macro to the compilation options.
157  void AddMacroDefinition(const std::string& name, const std::string& value) {
158    AddMacroDefinition(name.c_str(), name.size(), value.c_str(), value.size());
159  }
160
161  // Sets the compiler mode to generate debug information in the output.
162  void SetGenerateDebugInfo() {
163    shaderc_compile_options_set_generate_debug_info(options_);
164  }
165
166  // Sets the compiler optimization level to the given level. Only the last one
167  // takes effect if multiple calls of this function exist.
168  void SetOptimizationLevel(shaderc_optimization_level level) {
169    shaderc_compile_options_set_optimization_level(options_, level);
170  }
171
172  // A C++ version of the libshaderc includer interface.
173  class IncluderInterface {
174   public:
175    // Handles shaderc_include_resolver_fn callbacks.
176    virtual shaderc_include_result* GetInclude(const char* requested_source,
177                                               shaderc_include_type type,
178                                               const char* requesting_source,
179                                               size_t include_depth) = 0;
180
181    // Handles shaderc_include_result_release_fn callbacks.
182    virtual void ReleaseInclude(shaderc_include_result* data) = 0;
183  };
184
185  // Sets the includer instance for libshaderc to call during compilation, as
186  // described in shaderc_compile_options_set_include_callbacks().  Callbacks
187  // are routed to this includer's methods.
188  void SetIncluder(std::unique_ptr<IncluderInterface>&& includer) {
189    includer_ = std::move(includer);
190    shaderc_compile_options_set_include_callbacks(
191        options_,
192        [](void* user_data, const char* requested_source, int type,
193           const char* requesting_source, size_t include_depth) {
194          auto* includer = static_cast<IncluderInterface*>(user_data);
195          return includer->GetInclude(requested_source,
196                                      (shaderc_include_type)type,
197                                      requesting_source, include_depth);
198        },
199        [](void* user_data, shaderc_include_result* include_result) {
200          auto* includer = static_cast<IncluderInterface*>(user_data);
201          return includer->ReleaseInclude(include_result);
202        },
203        includer_.get());
204  }
205
206  // Forces the GLSL language version and profile to a given pair. The version
207  // number is the same as would appear in the #version annotation in the
208  // source. Version and profile specified here overrides the #version
209  // annotation in the source. Use profile: 'shaderc_profile_none' for GLSL
210  // versions that do not define profiles, e.g. versions below 150.
211  void SetForcedVersionProfile(int version, shaderc_profile profile) {
212    shaderc_compile_options_set_forced_version_profile(options_, version,
213                                                       profile);
214  }
215
216  // Sets the compiler mode to suppress warnings. Note this option overrides
217  // warnings-as-errors mode. When both suppress-warnings and warnings-as-errors
218  // modes are turned on, warning messages will be inhibited, and will not be
219  // emitted as error message.
220  void SetSuppressWarnings() {
221    shaderc_compile_options_set_suppress_warnings(options_);
222  }
223
224  // Sets the source language. The default is GLSL.
225  void SetSourceLanguage(shaderc_source_language lang) {
226    shaderc_compile_options_set_source_language(options_, lang);
227  }
228
229  // Sets the target shader environment, affecting which warnings or errors will
230  // be issued.
231  // The version will be for distinguishing between different versions of the
232  // target environment.
233  // "0" is the only supported version at this point
234  void SetTargetEnvironment(shaderc_target_env target, uint32_t version) {
235    shaderc_compile_options_set_target_env(options_, target, version);
236  }
237
238  // Sets the compiler mode to make all warnings into errors. Note the
239  // suppress-warnings mode overrides this option, i.e. if both
240  // warning-as-errors and suppress-warnings modes are set on, warnings will not
241  // be emitted as error message.
242  void SetWarningsAsErrors() {
243    shaderc_compile_options_set_warnings_as_errors(options_);
244  }
245
246  // Sets a resource limit.
247  void SetLimit(shaderc_limit limit, int value) {
248    shaderc_compile_options_set_limit(options_, limit, value);
249  }
250
251  // Sets whether the compiler should automatically assign bindings to uniforms
252  // that aren't already explicitly bound in the shader source.
253  void SetAutoBindUniforms(bool auto_bind) {
254    shaderc_compile_options_set_auto_bind_uniforms(options_, auto_bind);
255  }
256
257  // Sets whether the compiler should use HLSL IO mapping rules for bindings.
258  // Defaults to false.
259  void SetHlslIoMapping(bool hlsl_iomap) {
260    shaderc_compile_options_set_hlsl_io_mapping(options_, hlsl_iomap);
261  }
262
263  // Sets whether the compiler should determine block member offsets using HLSL
264  // packing rules instead of standard GLSL rules.  Defaults to false.  Only
265  // affects GLSL compilation.  HLSL rules are always used when compiling HLSL.
266  void SetHlslOffsets(bool hlsl_offsets) {
267    shaderc_compile_options_set_hlsl_offsets(options_, hlsl_offsets);
268  }
269
270  // Sets the base binding number used for for a uniform resource type when
271  // automatically assigning bindings.  For GLSL compilation, sets the lowest
272  // automatically assigned number.  For HLSL compilation, the regsiter number
273  // assigned to the resource is added to this specified base.
274  void SetBindingBase(shaderc_uniform_kind kind, uint32_t base) {
275    shaderc_compile_options_set_binding_base(options_, kind, base);
276  }
277
278  // Like SetBindingBase, but only takes effect when compiling a given shader
279  // stage.  The stage is assumed to be one of vertex, fragment, tessellation
280  // evaluation, tesselation control, geometry, or compute.
281  void SetBindingBaseForStage(shaderc_shader_kind shader_kind,
282                              shaderc_uniform_kind kind, uint32_t base) {
283    shaderc_compile_options_set_binding_base_for_stage(options_, shader_kind,
284                                                       kind, base);
285  }
286
287  // Sets a descriptor set and binding for an HLSL register in the given stage.
288  // Copies the parameter strings.
289  void SetHlslRegisterSetAndBindingForStage(shaderc_shader_kind shader_kind,
290                                            const std::string& reg,
291                                            const std::string& set,
292                                            const std::string& binding) {
293    shaderc_compile_options_set_hlsl_register_set_and_binding_for_stage(
294        options_, shader_kind, reg.c_str(), set.c_str(), binding.c_str());
295  }
296
297  // Sets a descriptor set and binding for an HLSL register in any stage.
298  // Copies the parameter strings.
299  void SetHlslRegisterSetAndBinding(const std::string& reg,
300                                    const std::string& set,
301                                    const std::string& binding) {
302    shaderc_compile_options_set_hlsl_register_set_and_binding(
303        options_, reg.c_str(), set.c_str(), binding.c_str());
304  }
305
306 private:
307  CompileOptions& operator=(const CompileOptions& other) = delete;
308  shaderc_compile_options_t options_;
309  std::unique_ptr<IncluderInterface> includer_;
310
311  friend class Compiler;
312};
313
314// The compilation context for compiling source to SPIR-V.
315class Compiler {
316 public:
317  Compiler() : compiler_(shaderc_compiler_initialize()) {}
318  ~Compiler() { shaderc_compiler_release(compiler_); }
319
320  Compiler(Compiler&& other) {
321    compiler_ = other.compiler_;
322    other.compiler_ = nullptr;
323  }
324
325  bool IsValid() const { return compiler_ != nullptr; }
326
327  // Compiles the given source GLSL and returns a SPIR-V binary module
328  // compilation result.
329  // The source_text parameter must be a valid pointer.
330  // The source_text_size parameter must be the length of the source text.
331  // The shader_kind parameter either forces the compilation to be done with a
332  // specified shader kind, or hint the compiler how to determine the exact
333  // shader kind. If the shader kind is set to shaderc_glslc_infer_from_source,
334  // the compiler will try to deduce the shader kind from the source string and
335  // a failure in this proess will generate an error. Currently only #pragma
336  // annotation is supported. If the shader kind is set to one of the default
337  // shader kinds, the compiler will fall back to the specified default shader
338  // kind in case it failed to deduce the shader kind from the source string.
339  // The input_file_name is a null-termintated string. It is used as a tag to
340  // identify the source string in cases like emitting error messages. It
341  // doesn't have to be a 'file name'.
342  // The entry_point_name parameter is a null-terminated string specifying
343  // the entry point name for HLSL compilation.  For GLSL compilation, the
344  // entry point name is assumed to be "main".
345  // The compilation is passed any options specified in the CompileOptions
346  // parameter.
347  // It is valid for the returned CompilationResult object to outlive this
348  // compiler object.
349  // Note when the options_ has disassembly mode or preprocessing only mode set
350  // on, the returned CompilationResult will hold a text string, instead of a
351  // SPIR-V binary generated with default options.
352  SpvCompilationResult CompileGlslToSpv(const char* source_text,
353                                        size_t source_text_size,
354                                        shaderc_shader_kind shader_kind,
355                                        const char* input_file_name,
356                                        const char* entry_point_name,
357                                        const CompileOptions& options) const {
358    shaderc_compilation_result_t compilation_result = shaderc_compile_into_spv(
359        compiler_, source_text, source_text_size, shader_kind, input_file_name,
360        entry_point_name, options.options_);
361    return SpvCompilationResult(compilation_result);
362  }
363
364  // Compiles the given source shader and returns a SPIR-V binary module
365  // compilation result.
366  // Like the first CompileGlslToSpv method but assumes the entry point name
367  // is "main".
368  SpvCompilationResult CompileGlslToSpv(const char* source_text,
369                                        size_t source_text_size,
370                                        shaderc_shader_kind shader_kind,
371                                        const char* input_file_name,
372                                        const CompileOptions& options) const {
373    return CompileGlslToSpv(source_text, source_text_size, shader_kind,
374                            input_file_name, "main", options);
375  }
376
377  // Compiles the given source GLSL and returns a SPIR-V binary module
378  // compilation result.
379  // Like the previous CompileGlslToSpv method but uses default options.
380  SpvCompilationResult CompileGlslToSpv(const char* source_text,
381                                        size_t source_text_size,
382                                        shaderc_shader_kind shader_kind,
383                                        const char* input_file_name) const {
384    shaderc_compilation_result_t compilation_result =
385        shaderc_compile_into_spv(compiler_, source_text, source_text_size,
386                                 shader_kind, input_file_name, "main", nullptr);
387    return SpvCompilationResult(compilation_result);
388  }
389
390  // Compiles the given source shader and returns a SPIR-V binary module
391  // compilation result.
392  // Like the first CompileGlslToSpv method but the source is provided as
393  // a std::string, and we assume the entry point is "main".
394  SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
395                                        shaderc_shader_kind shader_kind,
396                                        const char* input_file_name,
397                                        const CompileOptions& options) const {
398    return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
399                            input_file_name, options);
400  }
401
402  // Compiles the given source shader and returns a SPIR-V binary module
403  // compilation result.
404  // Like the first CompileGlslToSpv method but the source is provided as
405  // a std::string.
406  SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
407                                        shaderc_shader_kind shader_kind,
408                                        const char* input_file_name,
409                                        const char* entry_point_name,
410                                        const CompileOptions& options) const {
411    return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
412                            input_file_name, entry_point_name, options);
413  }
414
415  // Compiles the given source GLSL and returns a SPIR-V binary module
416  // compilation result.
417  // Like the previous CompileGlslToSpv method but assumes the entry point
418  // name is "main".
419  SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
420                                        shaderc_shader_kind shader_kind,
421                                        const char* input_file_name) const {
422    return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
423                            input_file_name);
424  }
425
426  // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
427  // compilation result.
428  // The assembly should follow the syntax defined in the SPIRV-Tools project
429  // (https://github.com/KhronosGroup/SPIRV-Tools/blob/master/syntax.md).
430  // It is valid for the returned CompilationResult object to outlive this
431  // compiler object.
432  // The assembling will pick options suitable for assembling specified in the
433  // CompileOptions parameter.
434  SpvCompilationResult AssembleToSpv(const char* source_assembly,
435                                     size_t source_assembly_size,
436                                     const CompileOptions& options) const {
437    return SpvCompilationResult(shaderc_assemble_into_spv(
438        compiler_, source_assembly, source_assembly_size, options.options_));
439  }
440
441  // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
442  // compilation result.
443  // Like the first AssembleToSpv method but uses the default compiler options.
444  SpvCompilationResult AssembleToSpv(const char* source_assembly,
445                                     size_t source_assembly_size) const {
446    return SpvCompilationResult(shaderc_assemble_into_spv(
447        compiler_, source_assembly, source_assembly_size, nullptr));
448  }
449
450  // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
451  // compilation result.
452  // Like the first AssembleToSpv method but the source is provided as a
453  // std::string.
454  SpvCompilationResult AssembleToSpv(const std::string& source_assembly,
455                                     const CompileOptions& options) const {
456    return SpvCompilationResult(
457        shaderc_assemble_into_spv(compiler_, source_assembly.data(),
458                                  source_assembly.size(), options.options_));
459  }
460
461  // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
462  // compilation result.
463  // Like the first AssembleToSpv method but the source is provided as a
464  // std::string and also uses default compiler options.
465  SpvCompilationResult AssembleToSpv(const std::string& source_assembly) const {
466    return SpvCompilationResult(shaderc_assemble_into_spv(
467        compiler_, source_assembly.data(), source_assembly.size(), nullptr));
468  }
469
470  // Compiles the given source GLSL and returns the SPIR-V assembly text
471  // compilation result.
472  // Options are similar to the first CompileToSpv method.
473  AssemblyCompilationResult CompileGlslToSpvAssembly(
474      const char* source_text, size_t source_text_size,
475      shaderc_shader_kind shader_kind, const char* input_file_name,
476      const char* entry_point_name, const CompileOptions& options) const {
477    shaderc_compilation_result_t compilation_result =
478        shaderc_compile_into_spv_assembly(
479            compiler_, source_text, source_text_size, shader_kind,
480            input_file_name, entry_point_name, options.options_);
481    return AssemblyCompilationResult(compilation_result);
482  }
483
484  // Compiles the given source GLSL and returns the SPIR-V assembly text
485  // compilation result.
486  // Similare to the previous method, but assumes entry point name is "main".
487  AssemblyCompilationResult CompileGlslToSpvAssembly(
488      const char* source_text, size_t source_text_size,
489      shaderc_shader_kind shader_kind, const char* input_file_name,
490      const CompileOptions& options) const {
491    return CompileGlslToSpvAssembly(source_text, source_text_size, shader_kind,
492                                    input_file_name, "main", options);
493  }
494
495  // Compiles the given source GLSL and returns the SPIR-V assembly text
496  // result. Like the first CompileGlslToSpvAssembly method but the source
497  // is provided as a std::string.  Options are otherwise similar to
498  // the first CompileToSpv method.
499  AssemblyCompilationResult CompileGlslToSpvAssembly(
500      const std::string& source_text, shaderc_shader_kind shader_kind,
501      const char* input_file_name, const char* entry_point_name,
502      const CompileOptions& options) const {
503    return CompileGlslToSpvAssembly(source_text.data(), source_text.size(),
504                                    shader_kind, input_file_name,
505                                    entry_point_name, options);
506  }
507
508  // Compiles the given source GLSL and returns the SPIR-V assembly text
509  // result. Like the previous  CompileGlslToSpvAssembly method but assumes
510  // the entry point name is "main".
511  AssemblyCompilationResult CompileGlslToSpvAssembly(
512      const std::string& source_text, shaderc_shader_kind shader_kind,
513      const char* input_file_name, const CompileOptions& options) const {
514    return CompileGlslToSpvAssembly(source_text, shader_kind, input_file_name,
515                                    "main", options);
516  }
517
518  // Preprocesses the given source GLSL and returns the preprocessed
519  // source text as a compilation result.
520  // Options are similar to the first CompileToSpv method.
521  PreprocessedSourceCompilationResult PreprocessGlsl(
522      const char* source_text, size_t source_text_size,
523      shaderc_shader_kind shader_kind, const char* input_file_name,
524      const CompileOptions& options) const {
525    shaderc_compilation_result_t compilation_result =
526        shaderc_compile_into_preprocessed_text(
527            compiler_, source_text, source_text_size, shader_kind,
528            input_file_name, "main", options.options_);
529    return PreprocessedSourceCompilationResult(compilation_result);
530  }
531
532  // Preprocesses the given source GLSL and returns text result.  Like the first
533  // PreprocessGlsl method but the source is provided as a std::string.
534  // Options are otherwise similar to the first CompileToSpv method.
535  PreprocessedSourceCompilationResult PreprocessGlsl(
536      const std::string& source_text, shaderc_shader_kind shader_kind,
537      const char* input_file_name, const CompileOptions& options) const {
538    return PreprocessGlsl(source_text.data(), source_text.size(), shader_kind,
539                          input_file_name, options);
540  }
541
542 private:
543  Compiler(const Compiler&) = delete;
544  Compiler& operator=(const Compiler& other) = delete;
545
546  shaderc_compiler_t compiler_;
547};
548}  // namespace shaderc
549
550#endif  // SHADERC_SHADERC_HPP_
551