Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/shaderc
Path: blob/main/glslc/src/file_compiler.cc
1560 views
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
#include "file_compiler.h"
16
17
#include <cassert>
18
#include <fstream>
19
#include <iomanip>
20
#include <iostream>
21
#include <sstream>
22
23
#if SHADERC_ENABLE_WGSL_OUTPUT == 1
24
#include "tint/tint.h"
25
#endif // SHADERC_ENABLE_WGSL_OUTPUT==1
26
27
#include "file.h"
28
#include "file_includer.h"
29
#include "shader_stage.h"
30
31
#include "libshaderc_util/io_shaderc.h"
32
#include "libshaderc_util/message.h"
33
34
namespace {
35
using shaderc_util::string_piece;
36
37
// A helper function to emit SPIR-V binary code as a list of hex numbers in
38
// text form. Returns true if a non-empty compilation result is emitted
39
// successfully. Return false if nothing should be emitted, either because the
40
// compilation result is empty, or the compilation output is not SPIR-V binary
41
// code.
42
template <typename CompilationResultType>
43
bool EmitSpirvBinaryAsCommaSeparatedNumbers(const CompilationResultType& result,
44
std::ostream* out) {
45
// Return early if the compilation output is not in SPIR-V binary code form.
46
if (!std::is_same<CompilationResultType,
47
shaderc::SpvCompilationResult>::value)
48
return false;
49
// Return early if the compilation result is empty.
50
if (result.cbegin() == result.cend()) return false;
51
std::ios::fmtflags output_stream_flag_cache(out->flags());
52
*out << std::hex << std::setfill('0');
53
auto RI = result.cbegin();
54
*out << "0x" << std::setw(8) << *RI++;
55
for (size_t counter = 1; RI != result.cend(); RI++, counter++) {
56
*out << ",";
57
// Break line for every four words.
58
if (counter % 4 == 0) {
59
*out << std::endl;
60
}
61
*out << "0x" << std::setw(8) << *RI;
62
}
63
out->flags(output_stream_flag_cache);
64
return true;
65
}
66
} // anonymous namespace
67
68
namespace glslc {
69
bool FileCompiler::CompileShaderFile(const InputFileSpec& input_file) {
70
std::vector<char> input_data;
71
std::string path = input_file.name;
72
if (!shaderc_util::ReadFile(path, &input_data)) {
73
return false;
74
}
75
76
std::string output_file_name = GetOutputFileName(input_file.name);
77
string_piece error_file_name = input_file.name;
78
79
if (error_file_name == "-") {
80
// If the input file was stdin, we want to output errors as <stdin>.
81
error_file_name = "<stdin>";
82
}
83
84
string_piece source_string = "";
85
if (!input_data.empty()) {
86
source_string = {&input_data.front(),
87
&input_data.front() + input_data.size()};
88
}
89
90
std::unique_ptr<FileIncluder> includer(
91
new FileIncluder(&include_file_finder_));
92
// Get a reference to the dependency trace before we pass the ownership to
93
// shaderc::CompileOptions.
94
const auto& used_source_files = includer->file_path_trace();
95
options_.SetIncluder(std::move(includer));
96
97
if (input_file.stage == shaderc_spirv_assembly) {
98
// Only act if the requested target is SPIR-V binary.
99
if (output_type_ == OutputType::SpirvBinary) {
100
const auto result =
101
compiler_.AssembleToSpv(source_string.data(), source_string.size());
102
return EmitCompiledResult(result, input_file.name, output_file_name,
103
error_file_name, used_source_files);
104
} else {
105
return true;
106
}
107
}
108
109
// Set the language. Since we only use the options object in this
110
// method, then it's ok to always set it without resetting it after
111
// compilation. A subsequent compilation will set it again anyway.
112
options_.SetSourceLanguage(input_file.language);
113
114
switch (output_type_) {
115
case OutputType::SpirvBinary: {
116
const auto result = compiler_.CompileGlslToSpv(
117
source_string.data(), source_string.size(), input_file.stage,
118
error_file_name.data(), input_file.entry_point_name.c_str(),
119
options_);
120
return EmitCompiledResult(result, input_file.name, output_file_name,
121
error_file_name, used_source_files);
122
}
123
case OutputType::SpirvAssemblyText: {
124
const auto result = compiler_.CompileGlslToSpvAssembly(
125
source_string.data(), source_string.size(), input_file.stage,
126
error_file_name.data(), input_file.entry_point_name.c_str(),
127
options_);
128
return EmitCompiledResult(result, input_file.name, output_file_name,
129
error_file_name, used_source_files);
130
}
131
case OutputType::PreprocessedText: {
132
const auto result = compiler_.PreprocessGlsl(
133
source_string.data(), source_string.size(), input_file.stage,
134
error_file_name.data(), options_);
135
return EmitCompiledResult(result, input_file.name, output_file_name,
136
error_file_name, used_source_files);
137
}
138
}
139
return false;
140
}
141
142
template <typename CompilationResultType>
143
bool FileCompiler::EmitCompiledResult(
144
const CompilationResultType& result, const std::string& input_file,
145
const std::string& output_file_name, string_piece error_file_name,
146
const std::unordered_set<std::string>& used_source_files) {
147
total_errors_ += result.GetNumErrors();
148
total_warnings_ += result.GetNumWarnings();
149
150
bool compilation_success =
151
result.GetCompilationStatus() == shaderc_compilation_status_success;
152
153
// Handle the error message for failing to deduce the shader kind.
154
if (result.GetCompilationStatus() ==
155
shaderc_compilation_status_invalid_stage) {
156
auto glsl_or_hlsl_extension = GetGlslOrHlslExtension(error_file_name);
157
if (glsl_or_hlsl_extension != "") {
158
std::cerr << "glslc: error: "
159
<< "'" << error_file_name << "': "
160
<< "." << glsl_or_hlsl_extension
161
<< " file encountered but no -fshader-stage specified ahead";
162
} else if (error_file_name == "<stdin>") {
163
std::cerr
164
<< "glslc: error: '-': -fshader-stage required when input is from "
165
"standard "
166
"input \"-\"";
167
} else {
168
std::cerr << "glslc: error: "
169
<< "'" << error_file_name << "': "
170
<< "file not recognized: File format not recognized";
171
}
172
std::cerr << "\n";
173
174
return false;
175
}
176
177
// Get a string_piece which refers to the normal compilation output for now.
178
// This string_piece might be redirected to the dependency info to be dumped
179
// later, if the handler is instantiated to dump as normal compilation output,
180
// and the original compilation output should be blocked. Otherwise it won't
181
// be touched. The main output stream dumps this string_piece later.
182
string_piece compilation_output(
183
reinterpret_cast<const char*>(result.cbegin()),
184
reinterpret_cast<const char*>(result.cend()));
185
186
// If we have dependency info dumping handler instantiated, we should dump
187
// dependency info first. This may redirect the compilation output
188
// string_piece to dependency info.
189
std::string potential_dependency_info_output;
190
if (dependency_info_dumping_handler_) {
191
if (!dependency_info_dumping_handler_->DumpDependencyInfo(
192
GetCandidateOutputFileName(input_file), error_file_name.data(),
193
&potential_dependency_info_output, used_source_files)) {
194
return false;
195
}
196
if (!potential_dependency_info_output.empty()) {
197
// If the potential_dependency_info_output string is not empty, it means
198
// we should dump dependency info as normal compilation output. Redirect
199
// the compilation output string_piece to the dependency info stored in
200
// potential_dependency_info_output to make it happen.
201
compilation_output = potential_dependency_info_output;
202
}
203
}
204
205
std::ostream* out = nullptr;
206
std::ofstream potential_file_stream;
207
if (compilation_success) {
208
out = shaderc_util::GetOutputStream(output_file_name,
209
&potential_file_stream, &std::cerr);
210
if (!out || out->fail()) {
211
// An error message has already been emitted to the stderr stream.
212
return false;
213
}
214
215
// Write compilation output to output file. If an output format for SPIR-V
216
// binary code is specified, it is handled here.
217
switch (binary_emission_format_) {
218
case SpirvBinaryEmissionFormat::Unspecified:
219
case SpirvBinaryEmissionFormat::Binary:
220
// The output format is unspecified or specified as binary output.
221
// On Windows, the output stream must be set to binary mode. By
222
// default the standard output stream is set to text mode, which
223
// translates newlines (\n) to carriage-return newline pairs
224
// (\r\n).
225
if (out == &std::cout) shaderc_util::FlushAndSetBinaryModeOnStdout();
226
out->write(compilation_output.data(), compilation_output.size());
227
if (out == &std::cout) shaderc_util::FlushAndSetTextModeOnStdout();
228
break;
229
case SpirvBinaryEmissionFormat::Numbers:
230
// The output format is specified to be a list of hex numbers, the
231
// compilation output must be in SPIR-V binary code form.
232
assert(output_type_ == OutputType::SpirvBinary);
233
if (EmitSpirvBinaryAsCommaSeparatedNumbers(result, out)) {
234
// Only emits the end-of-line character when the emitted compilation
235
// result is not empty.
236
*out << std::endl;
237
}
238
break;
239
case SpirvBinaryEmissionFormat::CInitList:
240
// The output format is specified to be a C-style initializer list, the
241
// compilation output must be in SPIR-V binary code form.
242
assert(output_type_ == OutputType::SpirvBinary);
243
if (result.begin() != result.end()) {
244
// Only emits the '{' when the compilation result is not empty.
245
*out << "{";
246
}
247
if (EmitSpirvBinaryAsCommaSeparatedNumbers(result, out)) {
248
// Only emits the end-of-line character when the emitted compilation
249
// result is not empty.
250
*out << "}" << std::endl;
251
}
252
break;
253
case SpirvBinaryEmissionFormat::WGSL: {
254
#if SHADERC_ENABLE_WGSL_OUTPUT == 1
255
tint::Context ctx;
256
tint::reader::spirv::Parser spv_reader(
257
&ctx, std::vector<uint32_t>(result.begin(), result.end()));
258
if (!spv_reader.Parse()) {
259
std::cout << "error: failed to convert SPIR-V binary to WGSL: "
260
<< spv_reader.error() << std::endl;
261
return false;
262
}
263
tint::writer::wgsl::Generator wgsl_writer(spv_reader.module());
264
if (!wgsl_writer.Generate()) {
265
std::cout << "error: failed to convert to WGSL: "
266
<< wgsl_writer.error() << std::endl;
267
return false;
268
}
269
*out << wgsl_writer.result();
270
#endif // SHADERC_ENABLE_WGSL_OUTPUT==1
271
break;
272
}
273
}
274
}
275
276
// Write error message to std::cerr.
277
std::cerr << result.GetErrorMessage();
278
if (out && out->fail()) {
279
// Something wrong happened on output.
280
if (out == &std::cout) {
281
std::cerr << "glslc: error: error writing to standard output"
282
<< std::endl;
283
} else {
284
std::cerr << "glslc: error: error writing to output file: '"
285
<< output_file_name_ << "'" << std::endl;
286
}
287
return false;
288
}
289
290
return compilation_success;
291
}
292
293
void FileCompiler::AddIncludeDirectory(const std::string& path) {
294
include_file_finder_.search_path().push_back(path);
295
}
296
297
void FileCompiler::SetIndividualCompilationFlag() {
298
if (output_type_ != OutputType::SpirvAssemblyText) {
299
needs_linking_ = false;
300
file_extension_ = ".spv";
301
}
302
}
303
304
void FileCompiler::SetDisassemblyFlag() {
305
if (!PreprocessingOnly()) {
306
output_type_ = OutputType::SpirvAssemblyText;
307
needs_linking_ = false;
308
file_extension_ = ".spvasm";
309
}
310
}
311
312
void FileCompiler::SetPreprocessingOnlyFlag() {
313
output_type_ = OutputType::PreprocessedText;
314
needs_linking_ = false;
315
if (output_file_name_.empty()) {
316
output_file_name_ = "-";
317
}
318
}
319
320
bool FileCompiler::ValidateOptions(size_t num_files) {
321
if (num_files == 0) {
322
std::cerr << "glslc: error: no input files" << std::endl;
323
return false;
324
}
325
326
if (num_files > 1 && needs_linking_) {
327
std::cerr << "glslc: error: linking multiple files is not supported yet. "
328
"Use -c to compile files individually."
329
<< std::endl;
330
return false;
331
}
332
333
// If we are outputting many object files, we cannot specify -o. Also
334
// if we are preprocessing multiple files they must be to stdout.
335
if (num_files > 1 && ((!PreprocessingOnly() && !needs_linking_ &&
336
!output_file_name_.empty()) ||
337
(PreprocessingOnly() && output_file_name_ != "-"))) {
338
std::cerr << "glslc: error: cannot specify -o when generating multiple"
339
" output files"
340
<< std::endl;
341
return false;
342
}
343
344
// If we have dependency info dumping handler instantiated, we should check
345
// its validity.
346
if (dependency_info_dumping_handler_) {
347
std::string dependency_info_dumping_hander_error_msg;
348
if (!dependency_info_dumping_handler_->IsValid(
349
&dependency_info_dumping_hander_error_msg, num_files)) {
350
std::cerr << "glslc: error: " << dependency_info_dumping_hander_error_msg
351
<< std::endl;
352
return false;
353
}
354
}
355
356
// If the output format is specified to be a binary, a list of hex numbers or
357
// a C-style initializer list, the output must be in SPIR-V binary code form.
358
if (binary_emission_format_ != SpirvBinaryEmissionFormat::Unspecified) {
359
if (output_type_ != OutputType::SpirvBinary) {
360
std::cerr << "glslc: error: cannot emit output as a ";
361
switch (binary_emission_format_) {
362
case SpirvBinaryEmissionFormat::Binary:
363
std::cerr << "binary";
364
break;
365
case SpirvBinaryEmissionFormat::Numbers:
366
std::cerr << "list of hex numbers";
367
break;
368
case SpirvBinaryEmissionFormat::CInitList:
369
std::cerr << "C-style initializer list";
370
break;
371
case SpirvBinaryEmissionFormat::WGSL:
372
std::cerr << "WGSL source program";
373
break;
374
case SpirvBinaryEmissionFormat::Unspecified:
375
// The compiler should never be here at runtime. This case is added to
376
// complete the switch cases.
377
break;
378
}
379
std::cerr << " when only preprocessing the source" << std::endl;
380
return false;
381
}
382
if (dependency_info_dumping_handler_ &&
383
dependency_info_dumping_handler_->DumpingAsCompilationOutput()) {
384
std::cerr << "glslc: error: cannot dump dependency info when specifying "
385
"any binary output format"
386
<< std::endl;
387
return false;
388
}
389
}
390
391
if (binary_emission_format_ == SpirvBinaryEmissionFormat::WGSL) {
392
#if SHADERC_ENABLE_WGSL_OUTPUT != 1
393
std::cerr << "glslc: error: can't output WGSL: glslc was built without "
394
"WGSL output support"
395
<< std::endl;
396
return false;
397
#endif
398
}
399
400
return true;
401
}
402
403
void FileCompiler::OutputMessages() {
404
shaderc_util::OutputMessages(&std::cerr, total_warnings_, total_errors_);
405
}
406
407
std::string FileCompiler::GetOutputFileName(std::string input_filename) {
408
if (output_file_name_.empty()) {
409
return needs_linking_ ? std::string("a.spv")
410
: GetCandidateOutputFileName(input_filename);
411
} else {
412
return output_file_name_.str();
413
}
414
}
415
416
std::string FileCompiler::GetCandidateOutputFileName(
417
std::string input_filename) {
418
if (!output_file_name_.empty() && !PreprocessingOnly()) {
419
return output_file_name_.str();
420
}
421
422
std::string extension = file_extension_;
423
if (PreprocessingOnly() || needs_linking_) {
424
extension = ".spv";
425
}
426
427
std::string candidate_output_file_name =
428
IsStageFile(input_filename)
429
? shaderc_util::GetBaseFileName(input_filename) + extension
430
: shaderc_util::GetBaseFileName(
431
input_filename.substr(0, input_filename.find_last_of('.')) +
432
extension);
433
return candidate_output_file_name;
434
}
435
} // namesapce glslc
436
437