aboutsummaryrefslogtreecommitdiff
path: root/libshaderc/src/shaderc.cc
blob: da0f3b2eae93b32e25e55c0497588ad1b829f695 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
// Copyright 2015 The Shaderc Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>
#include <cassert>
#include <cstdint>
#include <memory>
#include <sstream>
#include <vector>

#include "libshaderc_util/compiler.h"
#include "libshaderc_util/counting_includer.h"
#include "libshaderc_util/resources.h"
#include "libshaderc_util/spirv_tools_wrapper.h"
#include "libshaderc_util/version_profile.h"
#include "shaderc_private.h"
#include "spirv/unified1/spirv.hpp"

#if (defined(_MSC_VER) && !defined(_CPPUNWIND)) || !defined(__EXCEPTIONS)
#define TRY_IF_EXCEPTIONS_ENABLED
#define CATCH_IF_EXCEPTIONS_ENABLED(X) if (0)
#else
#define TRY_IF_EXCEPTIONS_ENABLED try
#define CATCH_IF_EXCEPTIONS_ENABLED(X) catch (X)
#endif

namespace {

// Returns shader stage (ie: vertex, fragment, etc.) in response to forced
// shader kinds. If the shader kind is not a forced kind, returns EshLangCount
// to let #pragma annotation or shader stage deducer determine the stage to
// use.
EShLanguage GetForcedStage(shaderc_shader_kind kind) {
  switch (kind) {
    case shaderc_glsl_vertex_shader:
      return EShLangVertex;
    case shaderc_glsl_fragment_shader:
      return EShLangFragment;
    case shaderc_glsl_compute_shader:
      return EShLangCompute;
    case shaderc_glsl_geometry_shader:
      return EShLangGeometry;
    case shaderc_glsl_tess_control_shader:
      return EShLangTessControl;
    case shaderc_glsl_tess_evaluation_shader:
      return EShLangTessEvaluation;

    case shaderc_glsl_raygen_shader:
      return EShLangRayGenNV;
    case shaderc_glsl_anyhit_shader:
      return EShLangAnyHitNV;
    case shaderc_glsl_closesthit_shader:
      return EShLangClosestHitNV;
    case shaderc_glsl_miss_shader:
      return EShLangMissNV;
    case shaderc_glsl_intersection_shader:
      return EShLangIntersectNV;
    case shaderc_glsl_callable_shader:
      return EShLangCallableNV;
    case shaderc_glsl_task_shader:
      return EShLangTaskNV;
    case shaderc_glsl_mesh_shader:
      return EShLangMeshNV;

    case shaderc_glsl_infer_from_source:
    case shaderc_glsl_default_vertex_shader:
    case shaderc_glsl_default_fragment_shader:
    case shaderc_glsl_default_compute_shader:
    case shaderc_glsl_default_geometry_shader:
    case shaderc_glsl_default_tess_control_shader:
    case shaderc_glsl_default_tess_evaluation_shader:
    case shaderc_glsl_default_raygen_shader:
    case shaderc_glsl_default_anyhit_shader:
    case shaderc_glsl_default_closesthit_shader:
    case shaderc_glsl_default_miss_shader:
    case shaderc_glsl_default_intersection_shader:
    case shaderc_glsl_default_callable_shader:
    case shaderc_glsl_default_task_shader:
    case shaderc_glsl_default_mesh_shader:
    case shaderc_spirv_assembly:
      return EShLangCount;
  }
  assert(0 && "Unhandled shaderc_shader_kind");
  return EShLangCount;
}

// A wrapper functor class to be used as stage deducer for libshaderc_util
// Compile() interface. When the given shader kind is one of the default shader
// kinds, this functor will be called if #pragma is not found in the source
// code. And it returns the corresponding shader stage. When the shader kind is
// a forced shader kind, this functor won't be called and it simply returns
// EShLangCount to make the syntax correct. When the shader kind is set to
// shaderc_glsl_deduce_from_pragma, this functor also returns EShLangCount, but
// the compiler should emit error if #pragma annotation is not found in this
// case.
class StageDeducer {
 public:
  explicit StageDeducer(
      shaderc_shader_kind kind = shaderc_glsl_infer_from_source)
      : kind_(kind), error_(false){}
  // The method that underlying glslang will call to determine the shader stage
  // to be used in current compilation. It is called only when there is neither
  // forced shader kind (or say stage, in the view of glslang), nor #pragma
  // annotation in the source code. This method transforms an user defined
  // 'default' shader kind to the corresponding shader stage. As this is the
  // last trial to determine the shader stage, failing to find the corresponding
  // shader stage will record an error.
  // Note that calling this method more than once during one compilation will
  // have the error recorded for the previous call been overwriten by the next
  // call.
  EShLanguage operator()(std::ostream* /*error_stream*/,
                         const shaderc_util::string_piece& /*error_tag*/) {
    EShLanguage stage = GetDefaultStage(kind_);
    if (stage == EShLangCount) {
      error_ = true;
    } else {
      error_ = false;
    }
    return stage;
  }

  // Returns true if there is error during shader stage deduction.
  bool error() const { return error_; }

 private:
  // Gets the corresponding shader stage for a given 'default' shader kind. All
  // other kinds are mapped to EShLangCount which should not be used.
  EShLanguage GetDefaultStage(shaderc_shader_kind kind) const {
    switch (kind) {
      case shaderc_glsl_vertex_shader:
      case shaderc_glsl_fragment_shader:
      case shaderc_glsl_compute_shader:
      case shaderc_glsl_geometry_shader:
      case shaderc_glsl_tess_control_shader:
      case shaderc_glsl_tess_evaluation_shader:
      case shaderc_glsl_infer_from_source:
      case shaderc_glsl_raygen_shader:
      case shaderc_glsl_anyhit_shader:
      case shaderc_glsl_closesthit_shader:
      case shaderc_glsl_miss_shader:
      case shaderc_glsl_intersection_shader:
      case shaderc_glsl_callable_shader:
      case shaderc_glsl_task_shader:
      case shaderc_glsl_mesh_shader:
        return EShLangCount;
      case shaderc_glsl_default_vertex_shader:
        return EShLangVertex;
      case shaderc_glsl_default_fragment_shader:
        return EShLangFragment;
      case shaderc_glsl_default_compute_shader:
        return EShLangCompute;
      case shaderc_glsl_default_geometry_shader:
        return EShLangGeometry;
      case shaderc_glsl_default_tess_control_shader:
        return EShLangTessControl;
      case shaderc_glsl_default_tess_evaluation_shader:
        return EShLangTessEvaluation;
      case shaderc_glsl_default_raygen_shader:
        return EShLangRayGenNV;
      case shaderc_glsl_default_anyhit_shader:
        return EShLangAnyHitNV;
      case shaderc_glsl_default_closesthit_shader:
        return EShLangClosestHitNV;
      case shaderc_glsl_default_miss_shader:
        return EShLangMissNV;
      case shaderc_glsl_default_intersection_shader:
        return EShLangIntersectNV;
      case shaderc_glsl_default_callable_shader:
        return EShLangCallableNV;
      case shaderc_glsl_default_task_shader:
        return EShLangTaskNV;
      case shaderc_glsl_default_mesh_shader:
        return EShLangMeshNV;
      case shaderc_spirv_assembly:
        return EShLangCount;
    }
    assert(0 && "Unhandled shaderc_shader_kind");
    return EShLangCount;
  }

  shaderc_shader_kind kind_;
  bool error_;
};

// A bridge between the libshaderc includer and libshaderc_util includer.
class InternalFileIncluder : public shaderc_util::CountingIncluder {
 public:
  InternalFileIncluder(const shaderc_include_resolve_fn resolver,
                       const shaderc_include_result_release_fn result_releaser,
                       void* user_data)
      : resolver_(resolver),
        result_releaser_(result_releaser),
        user_data_(user_data){}
  InternalFileIncluder()
      : resolver_(nullptr), result_releaser_(nullptr), user_data_(nullptr){}

 private:
  // Check the validity of the callbacks.
  bool AreValidCallbacks() const {
    return resolver_ != nullptr && result_releaser_ != nullptr;
  }

  // Maps CountingIncluder IncludeType value to a shaderc_include_type
  // value.
  shaderc_include_type GetIncludeType(IncludeType type) {
    switch (type) {
      case IncludeType::Local:
        return shaderc_include_type_relative;
      case IncludeType::System:
        return shaderc_include_type_standard;
      default:
        break;
    }
    assert(0 && "Unhandled IncludeType");
    return shaderc_include_type_relative;
  }

  // Resolves an include request for the requested source of the given
  // type in the context of the specified requesting source.  On success,
  // returns a newly allocated IncludeResponse containing the fully resolved
  // name of the requested source and the contents of that source.
  // On failure, returns a newly allocated IncludeResponse where the
  // resolved name member is an empty string, and the contents members
  // contains error details.
  virtual glslang::TShader::Includer::IncludeResult* include_delegate(
      const char* requested_source, const char* requesting_source,
      IncludeType type, size_t include_depth) override {
    if (!AreValidCallbacks()) {
      static const char kUnexpectedIncludeError[] =
          "#error unexpected include directive";
      return new glslang::TShader::Includer::IncludeResult{
          "", kUnexpectedIncludeError, strlen(kUnexpectedIncludeError),
          nullptr};
    }
    shaderc_include_result* include_result =
        resolver_(user_data_, requested_source, GetIncludeType(type),
                  requesting_source, include_depth);
    // Make a glslang IncludeResult from a shaderc_include_result.  The
    // user_data member of the IncludeResult is a pointer to the
    // shaderc_include_result object, so we can later release the latter.
    return new glslang::TShader::Includer::IncludeResult{
        std::string(include_result->source_name,
                    include_result->source_name_length),
        include_result->content, include_result->content_length,
        include_result};
  }

  // Releases the given IncludeResult.
  virtual void release_delegate(
      glslang::TShader::Includer::IncludeResult* result) override {
    if (result && result_releaser_) {
      result_releaser_(user_data_,
                       static_cast<shaderc_include_result*>(result->userData));
    }
    delete result;
  }

  const shaderc_include_resolve_fn resolver_;
  const shaderc_include_result_release_fn result_releaser_;
  void* user_data_;
};

// Converts the target env to the corresponding one in shaderc_util::Compiler.
shaderc_util::Compiler::TargetEnv GetCompilerTargetEnv(shaderc_target_env env) {
  switch (env) {
    case shaderc_target_env_opengl:
      return shaderc_util::Compiler::TargetEnv::OpenGL;
    case shaderc_target_env_opengl_compat:
      return shaderc_util::Compiler::TargetEnv::OpenGLCompat;
    case shaderc_target_env_webgpu:
      assert(false);
      break;
    case shaderc_target_env_vulkan:
    default:
      break;
  }

  return shaderc_util::Compiler::TargetEnv::Vulkan;
}

shaderc_util::Compiler::TargetEnvVersion GetCompilerTargetEnvVersion(
    uint32_t version_number) {
  using namespace shaderc_util;

  if (static_cast<uint32_t>(Compiler::TargetEnvVersion::Vulkan_1_0) ==
      version_number) {
    return Compiler::TargetEnvVersion::Vulkan_1_0;
  }
  if (static_cast<uint32_t>(Compiler::TargetEnvVersion::Vulkan_1_1) ==
      version_number) {
    return Compiler::TargetEnvVersion::Vulkan_1_1;
  }
  if (static_cast<uint32_t>(Compiler::TargetEnvVersion::Vulkan_1_2) ==
      version_number) {
    return Compiler::TargetEnvVersion::Vulkan_1_2;
  }
  if (static_cast<uint32_t>(Compiler::TargetEnvVersion::OpenGL_4_5) ==
      version_number) {
    return Compiler::TargetEnvVersion::OpenGL_4_5;
  }

  return Compiler::TargetEnvVersion::Default;
}

// Returns the Compiler::Limit enum for the given shaderc_limit enum.
shaderc_util::Compiler::Limit CompilerLimit(shaderc_limit limit) {
  switch (limit) {
#define RESOURCE(NAME, FIELD, CNAME) \
  case shaderc_limit_##CNAME:        \
    return shaderc_util::Compiler::Limit::NAME;
#include "libshaderc_util/resources.inc"
#undef RESOURCE
    default:
      break;
  }
  assert(0 && "Should not have reached here");
  return static_cast<shaderc_util::Compiler::Limit>(0);
}

// Returns the Compiler::UniformKind for the given shaderc_uniform_kind.
shaderc_util::Compiler::UniformKind GetUniformKind(shaderc_uniform_kind kind) {
  switch (kind) {
    case shaderc_uniform_kind_texture:
      return shaderc_util::Compiler::UniformKind::Texture;
    case shaderc_uniform_kind_sampler:
      return shaderc_util::Compiler::UniformKind::Sampler;
    case shaderc_uniform_kind_image:
      return shaderc_util::Compiler::UniformKind::Image;
    case shaderc_uniform_kind_buffer:
      return shaderc_util::Compiler::UniformKind::Buffer;
    case shaderc_uniform_kind_storage_buffer:
      return shaderc_util::Compiler::UniformKind::StorageBuffer;
    case shaderc_uniform_kind_unordered_access_view:
      return shaderc_util::Compiler::UniformKind::UnorderedAccessView;
  }
  assert(0 && "Should not have reached here");
  return static_cast<shaderc_util::Compiler::UniformKind>(0);
}

// Returns the Compiler::Stage for generic stage values in shaderc_shader_kind.
shaderc_util::Compiler::Stage GetStage(shaderc_shader_kind kind) {
  switch (kind) {
    case shaderc_vertex_shader:
      return shaderc_util::Compiler::Stage::Vertex;
    case shaderc_fragment_shader:
      return shaderc_util::Compiler::Stage::Fragment;
    case shaderc_compute_shader:
      return shaderc_util::Compiler::Stage::Compute;
    case shaderc_tess_control_shader:
      return shaderc_util::Compiler::Stage::TessControl;
    case shaderc_tess_evaluation_shader:
      return shaderc_util::Compiler::Stage::TessEval;
    case shaderc_geometry_shader:
      return shaderc_util::Compiler::Stage::Geometry;
    default:
      break;
  }
  assert(0 && "Should not have reached here");
  return static_cast<shaderc_util::Compiler::Stage>(0);
}

}  // anonymous namespace

struct shaderc_compile_options {
  shaderc_target_env target_env = shaderc_target_env_default;
  uint32_t target_env_version = 0;
  shaderc_util::Compiler compiler;
  shaderc_include_resolve_fn include_resolver = nullptr;
  shaderc_include_result_release_fn include_result_releaser = nullptr;
  void* include_user_data = nullptr;
};

shaderc_compile_options_t shaderc_compile_options_initialize() {
  return new (std::nothrow) shaderc_compile_options;
}

shaderc_compile_options_t shaderc_compile_options_clone(
    const shaderc_compile_options_t options) {
  if (!options) {
    return shaderc_compile_options_initialize();
  }
  return new (std::nothrow) shaderc_compile_options(*options);
}

void shaderc_compile_options_release(shaderc_compile_options_t options) {
  delete options;
}

void shaderc_compile_options_add_macro_definition(
    shaderc_compile_options_t options, const char* name, size_t name_length,
    const char* value, size_t value_length) {
  options->compiler.AddMacroDefinition(name, name_length, value, value_length);
}

void shaderc_compile_options_set_source_language(
    shaderc_compile_options_t options, shaderc_source_language set_lang) {
  auto lang = shaderc_util::Compiler::SourceLanguage::GLSL;
  if (set_lang == shaderc_source_language_hlsl)
    lang = shaderc_util::Compiler::SourceLanguage::HLSL;
  options->compiler.SetSourceLanguage(lang);
}

void shaderc_compile_options_set_generate_debug_info(
    shaderc_compile_options_t options) {
  options->compiler.SetGenerateDebugInfo();
}

void shaderc_compile_options_set_optimization_level(
    shaderc_compile_options_t options, shaderc_optimization_level level) {
  auto opt_level = shaderc_util::Compiler::OptimizationLevel::Zero;
  switch (level) {
    case shaderc_optimization_level_size:
      opt_level = shaderc_util::Compiler::OptimizationLevel::Size;
      break;
    case shaderc_optimization_level_performance:
      opt_level = shaderc_util::Compiler::OptimizationLevel::Performance;
      break;
    default:
      break;
  }

  options->compiler.SetOptimizationLevel(opt_level);
}

void shaderc_compile_options_set_forced_version_profile(
    shaderc_compile_options_t options, int version, shaderc_profile profile) {
  // Transfer the profile parameter from public enum type to glslang internal
  // enum type. No default case here so that compiler will complain if new enum
  // member is added later but not handled here.
  switch (profile) {
    case shaderc_profile_none:
      options->compiler.SetForcedVersionProfile(version, ENoProfile);
      break;
    case shaderc_profile_core:
      options->compiler.SetForcedVersionProfile(version, ECoreProfile);
      break;
    case shaderc_profile_compatibility:
      options->compiler.SetForcedVersionProfile(version, ECompatibilityProfile);
      break;
    case shaderc_profile_es:
      options->compiler.SetForcedVersionProfile(version, EEsProfile);
      break;
  }
}

void shaderc_compile_options_set_include_callbacks(
    shaderc_compile_options_t options, shaderc_include_resolve_fn resolver,
    shaderc_include_result_release_fn result_releaser, void* user_data) {
  options->include_resolver = resolver;
  options->include_result_releaser = result_releaser;
  options->include_user_data = user_data;
}

void shaderc_compile_options_set_suppress_warnings(
    shaderc_compile_options_t options) {
  options->compiler.SetSuppressWarnings();
}

void shaderc_compile_options_set_target_env(shaderc_compile_options_t options,
                                            shaderc_target_env target,
                                            uint32_t version) {
  options->target_env = target;
  options->compiler.SetTargetEnv(GetCompilerTargetEnv(target),
                                 GetCompilerTargetEnvVersion(version));
}

void shaderc_compile_options_set_target_spirv(shaderc_compile_options_t options,
                                              shaderc_spirv_version ver) {
  // We made the values match, so we can get away with a static cast.
  options->compiler.SetTargetSpirv(
      static_cast<shaderc_util::Compiler::SpirvVersion>(ver));
}

void shaderc_compile_options_set_warnings_as_errors(
    shaderc_compile_options_t options) {
  options->compiler.SetWarningsAsErrors();
}

void shaderc_compile_options_set_limit(shaderc_compile_options_t options,
                                       shaderc_limit limit, int value) {
  options->compiler.SetLimit(CompilerLimit(limit), value);
}

void shaderc_compile_options_set_auto_bind_uniforms(
    shaderc_compile_options_t options, bool auto_bind) {
  options->compiler.SetAutoBindUniforms(auto_bind);
}

void shaderc_compile_options_set_hlsl_io_mapping(
    shaderc_compile_options_t options, bool hlsl_iomap) {
  options->compiler.SetHlslIoMapping(hlsl_iomap);
}

void shaderc_compile_options_set_hlsl_offsets(shaderc_compile_options_t options,
                                              bool hlsl_offsets) {
  options->compiler.SetHlslOffsets(hlsl_offsets);
}

void shaderc_compile_options_set_binding_base(shaderc_compile_options_t options,
                                              shaderc_uniform_kind kind,
                                              uint32_t base) {
  options->compiler.SetAutoBindingBase(GetUniformKind(kind), base);
}

void shaderc_compile_options_set_binding_base_for_stage(
    shaderc_compile_options_t options, shaderc_shader_kind shader_kind,
    shaderc_uniform_kind kind, uint32_t base) {
  options->compiler.SetAutoBindingBaseForStage(GetStage(shader_kind),
                                               GetUniformKind(kind), base);
}

void shaderc_compile_options_set_auto_map_locations(
    shaderc_compile_options_t options, bool auto_map) {
  options->compiler.SetAutoMapLocations(auto_map);
}

void shaderc_compile_options_set_hlsl_register_set_and_binding_for_stage(
    shaderc_compile_options_t options, shaderc_shader_kind shader_kind,
    const char* reg, const char* set, const char* binding) {
  options->compiler.SetHlslRegisterSetAndBindingForStage(GetStage(shader_kind),
                                                         reg, set, binding);
}

void shaderc_compile_options_set_hlsl_register_set_and_binding(
    shaderc_compile_options_t options, const char* reg, const char* set,
    const char* binding) {
  options->compiler.SetHlslRegisterSetAndBinding(reg, set, binding);
}

void shaderc_compile_options_set_hlsl_functionality1(
    shaderc_compile_options_t options, bool enable) {
  options->compiler.EnableHlslFunctionality1(enable);
}

void shaderc_compile_options_set_invert_y(
    shaderc_compile_options_t options, bool enable) {
  options->compiler.EnableInvertY(enable);
}

void shaderc_compile_options_set_nan_clamp(shaderc_compile_options_t options,
                                           bool enable) {
  options->compiler.SetNanClamp(enable);
}

shaderc_compiler_t shaderc_compiler_initialize() {
  shaderc_compiler_t compiler = new (std::nothrow) shaderc_compiler;
  if (compiler) {
    compiler->initializer.reset(new shaderc_util::GlslangInitializer);
  }
  return compiler;
}

void shaderc_compiler_release(shaderc_compiler_t compiler) {
  delete compiler;
}

namespace {
shaderc_compilation_result_t CompileToSpecifiedOutputType(
    const shaderc_compiler_t compiler, const char* source_text,
    size_t source_text_size, shaderc_shader_kind shader_kind,
    const char* input_file_name, const char* entry_point_name,
    const shaderc_compile_options_t additional_options,
    shaderc_util::Compiler::OutputType output_type) {
  auto* result = new (std::nothrow) shaderc_compilation_result_vector;
  if (!result) return nullptr;

  if (!input_file_name) {
    result->messages = "Input file name string was null.";
    result->num_errors = 1;
    result->compilation_status = shaderc_compilation_status_compilation_error;
    return result;
  }
  result->compilation_status = shaderc_compilation_status_invalid_stage;
  bool compilation_succeeded = false;  // In case we exit early.
  std::vector<uint32_t> compilation_output_data;
  size_t compilation_output_data_size_in_bytes = 0u;
  if (!compiler->initializer) return result;
  TRY_IF_EXCEPTIONS_ENABLED {
    std::stringstream errors;
    size_t total_warnings = 0;
    size_t total_errors = 0;
    std::string input_file_name_str(input_file_name);
    EShLanguage forced_stage = GetForcedStage(shader_kind);
    shaderc_util::string_piece source_string =
        shaderc_util::string_piece(source_text, source_text + source_text_size);
    StageDeducer stage_deducer(shader_kind);
    if (additional_options) {
      InternalFileIncluder includer(additional_options->include_resolver,
                                    additional_options->include_result_releaser,
                                    additional_options->include_user_data);
      // Depends on return value optimization to avoid extra copy.
      std::tie(compilation_succeeded, compilation_output_data,
               compilation_output_data_size_in_bytes) =
          additional_options->compiler.Compile(
              source_string, forced_stage, input_file_name_str, entry_point_name,
              // stage_deducer has a flag: error_, which we need to check later.
              // We need to make this a reference wrapper, so that std::function
              // won't make a copy for this callable object.
              std::ref(stage_deducer), includer, output_type, &errors,
              &total_warnings, &total_errors);
    } else {
      // Compile with default options.
      InternalFileIncluder includer;
      std::tie(compilation_succeeded, compilation_output_data,
               compilation_output_data_size_in_bytes) =
          shaderc_util::Compiler().Compile(
              source_string, forced_stage, input_file_name_str, entry_point_name,
              std::ref(stage_deducer), includer, output_type, &errors,
              &total_warnings, &total_errors);
    }

    result->messages = errors.str();
    result->SetOutputData(std::move(compilation_output_data));
    result->output_data_size = compilation_output_data_size_in_bytes;
    result->num_warnings = total_warnings;
    result->num_errors = total_errors;
    if (compilation_succeeded) {
      result->compilation_status = shaderc_compilation_status_success;
    } else {
      // Check whether the error is caused by failing to deduce the shader
      // stage. If it is the case, set the error type to shader kind error.
      // Otherwise, set it to compilation error.
      result->compilation_status =
          stage_deducer.error() ? shaderc_compilation_status_invalid_stage
                                : shaderc_compilation_status_compilation_error;
    }
  }
  CATCH_IF_EXCEPTIONS_ENABLED(...) {
    result->compilation_status = shaderc_compilation_status_internal_error;
  }
  return result;
}
}  // anonymous namespace

shaderc_compilation_result_t shaderc_compile_into_spv(
    const shaderc_compiler_t compiler, const char* source_text,
    size_t source_text_size, shaderc_shader_kind shader_kind,
    const char* input_file_name, const char* entry_point_name,
    const shaderc_compile_options_t additional_options) {
  return CompileToSpecifiedOutputType(
      compiler, source_text, source_text_size, shader_kind, input_file_name,
      entry_point_name, additional_options,
      shaderc_util::Compiler::OutputType::SpirvBinary);
}

shaderc_compilation_result_t shaderc_compile_into_spv_assembly(
    const shaderc_compiler_t compiler, const char* source_text,
    size_t source_text_size, shaderc_shader_kind shader_kind,
    const char* input_file_name, const char* entry_point_name,
    const shaderc_compile_options_t additional_options) {
  return CompileToSpecifiedOutputType(
      compiler, source_text, source_text_size, shader_kind, input_file_name,
      entry_point_name, additional_options,
      shaderc_util::Compiler::OutputType::SpirvAssemblyText);
}

shaderc_compilation_result_t shaderc_compile_into_preprocessed_text(
    const shaderc_compiler_t compiler, const char* source_text,
    size_t source_text_size, shaderc_shader_kind shader_kind,
    const char* input_file_name, const char* entry_point_name,
    const shaderc_compile_options_t additional_options) {
  return CompileToSpecifiedOutputType(
      compiler, source_text, source_text_size, shader_kind, input_file_name,
      entry_point_name, additional_options,
      shaderc_util::Compiler::OutputType::PreprocessedText);
}

shaderc_compilation_result_t shaderc_assemble_into_spv(
    const shaderc_compiler_t compiler, const char* source_assembly,
    size_t source_assembly_size,
    const shaderc_compile_options_t additional_options) {
  auto* result = new (std::nothrow) shaderc_compilation_result_spv_binary;
  if (!result) return nullptr;
  result->compilation_status = shaderc_compilation_status_invalid_assembly;
  if (!compiler->initializer) return result;
  if (source_assembly == nullptr) return result;

  TRY_IF_EXCEPTIONS_ENABLED {
    spv_binary assembling_output_data = nullptr;
    std::string errors;
    const auto target_env = additional_options ? additional_options->target_env
                                               : shaderc_target_env_default;
    const uint32_t target_env_version =
        additional_options ? additional_options->target_env_version : 0;
    const bool assembling_succeeded = shaderc_util::SpirvToolsAssemble(
        GetCompilerTargetEnv(target_env),
        GetCompilerTargetEnvVersion(target_env_version),
        {source_assembly, source_assembly + source_assembly_size},
        &assembling_output_data, &errors);
    result->num_errors = !assembling_succeeded;
    if (assembling_succeeded) {
      result->SetOutputData(assembling_output_data);
      result->output_data_size =
          assembling_output_data->wordCount * sizeof(uint32_t);
      result->compilation_status = shaderc_compilation_status_success;
    } else {
      result->messages = std::move(errors);
      result->compilation_status = shaderc_compilation_status_invalid_assembly;
    }
  }
  CATCH_IF_EXCEPTIONS_ENABLED(...) {
    result->compilation_status = shaderc_compilation_status_internal_error;
  }

  return result;
}

size_t shaderc_result_get_length(const shaderc_compilation_result_t result) {
  return result->output_data_size;
}

size_t shaderc_result_get_num_warnings(
    const shaderc_compilation_result_t result) {
  return result->num_warnings;
}

size_t shaderc_result_get_num_errors(
    const shaderc_compilation_result_t result) {
  return result->num_errors;
}

const char* shaderc_result_get_bytes(
    const shaderc_compilation_result_t result) {
  return result->GetBytes();
}

void shaderc_result_release(shaderc_compilation_result_t result) {
  delete result;
}

const char* shaderc_result_get_error_message(
    const shaderc_compilation_result_t result) {
  return result->messages.c_str();
}

shaderc_compilation_status shaderc_result_get_compilation_status(
    const shaderc_compilation_result_t result) {
  return result->compilation_status;
}

void shaderc_get_spv_version(unsigned int* version, unsigned int* revision) {
  *version = spv::Version;
  *revision = spv::Revision;
}

bool shaderc_parse_version_profile(const char* str, int* version,
                                   shaderc_profile* profile) {
  EProfile glslang_profile;
  bool success = shaderc_util::ParseVersionProfile(
      std::string(str, strlen(str)), version, &glslang_profile);
  if (!success) return false;

  switch (glslang_profile) {
    case EEsProfile:
      *profile = shaderc_profile_es;
      return true;
    case ECoreProfile:
      *profile = shaderc_profile_core;
      return true;
    case ECompatibilityProfile:
      *profile = shaderc_profile_compatibility;
      return true;
    case ENoProfile:
      *profile = shaderc_profile_none;
      return true;
    case EBadProfile:
    case EProfileCount:
      return false;
  }

  // Shouldn't reach here, all profile enum should be handled above.
  // Be strict to return false.
  return false;
}