1 //
2 // Copyright (C) 2019 Google, Inc.
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 // Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //
13 // Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following
15 // disclaimer in the documentation and/or other materials provided
16 // with the distribution.
17 //
18 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19 // contributors may be used to endorse or promote products derived
20 // from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 // POSSIBILITY OF SUCH DAMAGE.
34 //
35
36 #include <cstdio>
37 #include <cstdint>
38 #include <memory>
39
40 #ifdef __EMSCRIPTEN__
41 #include <emscripten.h>
42 #endif
43
44 #include "../../../SPIRV/GlslangToSpv.h"
45 #include "../../../glslang/Public/ShaderLang.h"
46
47 #ifndef __EMSCRIPTEN__
48 #define EMSCRIPTEN_KEEPALIVE
49 #endif
50
51 const TBuiltInResource DefaultTBuiltInResource = {
52 /* .MaxLights = */ 32,
53 /* .MaxClipPlanes = */ 6,
54 /* .MaxTextureUnits = */ 32,
55 /* .MaxTextureCoords = */ 32,
56 /* .MaxVertexAttribs = */ 64,
57 /* .MaxVertexUniformComponents = */ 4096,
58 /* .MaxVaryingFloats = */ 64,
59 /* .MaxVertexTextureImageUnits = */ 32,
60 /* .MaxCombinedTextureImageUnits = */ 80,
61 /* .MaxTextureImageUnits = */ 32,
62 /* .MaxFragmentUniformComponents = */ 4096,
63 /* .MaxDrawBuffers = */ 32,
64 /* .MaxVertexUniformVectors = */ 128,
65 /* .MaxVaryingVectors = */ 8,
66 /* .MaxFragmentUniformVectors = */ 16,
67 /* .MaxVertexOutputVectors = */ 16,
68 /* .MaxFragmentInputVectors = */ 15,
69 /* .MinProgramTexelOffset = */ -8,
70 /* .MaxProgramTexelOffset = */ 7,
71 /* .MaxClipDistances = */ 8,
72 /* .MaxComputeWorkGroupCountX = */ 65535,
73 /* .MaxComputeWorkGroupCountY = */ 65535,
74 /* .MaxComputeWorkGroupCountZ = */ 65535,
75 /* .MaxComputeWorkGroupSizeX = */ 1024,
76 /* .MaxComputeWorkGroupSizeY = */ 1024,
77 /* .MaxComputeWorkGroupSizeZ = */ 64,
78 /* .MaxComputeUniformComponents = */ 1024,
79 /* .MaxComputeTextureImageUnits = */ 16,
80 /* .MaxComputeImageUniforms = */ 8,
81 /* .MaxComputeAtomicCounters = */ 8,
82 /* .MaxComputeAtomicCounterBuffers = */ 1,
83 /* .MaxVaryingComponents = */ 60,
84 /* .MaxVertexOutputComponents = */ 64,
85 /* .MaxGeometryInputComponents = */ 64,
86 /* .MaxGeometryOutputComponents = */ 128,
87 /* .MaxFragmentInputComponents = */ 128,
88 /* .MaxImageUnits = */ 8,
89 /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
90 /* .MaxCombinedShaderOutputResources = */ 8,
91 /* .MaxImageSamples = */ 0,
92 /* .MaxVertexImageUniforms = */ 0,
93 /* .MaxTessControlImageUniforms = */ 0,
94 /* .MaxTessEvaluationImageUniforms = */ 0,
95 /* .MaxGeometryImageUniforms = */ 0,
96 /* .MaxFragmentImageUniforms = */ 8,
97 /* .MaxCombinedImageUniforms = */ 8,
98 /* .MaxGeometryTextureImageUnits = */ 16,
99 /* .MaxGeometryOutputVertices = */ 256,
100 /* .MaxGeometryTotalOutputComponents = */ 1024,
101 /* .MaxGeometryUniformComponents = */ 1024,
102 /* .MaxGeometryVaryingComponents = */ 64,
103 /* .MaxTessControlInputComponents = */ 128,
104 /* .MaxTessControlOutputComponents = */ 128,
105 /* .MaxTessControlTextureImageUnits = */ 16,
106 /* .MaxTessControlUniformComponents = */ 1024,
107 /* .MaxTessControlTotalOutputComponents = */ 4096,
108 /* .MaxTessEvaluationInputComponents = */ 128,
109 /* .MaxTessEvaluationOutputComponents = */ 128,
110 /* .MaxTessEvaluationTextureImageUnits = */ 16,
111 /* .MaxTessEvaluationUniformComponents = */ 1024,
112 /* .MaxTessPatchComponents = */ 120,
113 /* .MaxPatchVertices = */ 32,
114 /* .MaxTessGenLevel = */ 64,
115 /* .MaxViewports = */ 16,
116 /* .MaxVertexAtomicCounters = */ 0,
117 /* .MaxTessControlAtomicCounters = */ 0,
118 /* .MaxTessEvaluationAtomicCounters = */ 0,
119 /* .MaxGeometryAtomicCounters = */ 0,
120 /* .MaxFragmentAtomicCounters = */ 8,
121 /* .MaxCombinedAtomicCounters = */ 8,
122 /* .MaxAtomicCounterBindings = */ 1,
123 /* .MaxVertexAtomicCounterBuffers = */ 0,
124 /* .MaxTessControlAtomicCounterBuffers = */ 0,
125 /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
126 /* .MaxGeometryAtomicCounterBuffers = */ 0,
127 /* .MaxFragmentAtomicCounterBuffers = */ 1,
128 /* .MaxCombinedAtomicCounterBuffers = */ 1,
129 /* .MaxAtomicCounterBufferSize = */ 16384,
130 /* .MaxTransformFeedbackBuffers = */ 4,
131 /* .MaxTransformFeedbackInterleavedComponents = */ 64,
132 /* .MaxCullDistances = */ 8,
133 /* .MaxCombinedClipAndCullDistances = */ 8,
134 /* .MaxSamples = */ 4,
135 /* .maxMeshOutputVerticesNV = */ 256,
136 /* .maxMeshOutputPrimitivesNV = */ 512,
137 /* .maxMeshWorkGroupSizeX_NV = */ 32,
138 /* .maxMeshWorkGroupSizeY_NV = */ 1,
139 /* .maxMeshWorkGroupSizeZ_NV = */ 1,
140 /* .maxTaskWorkGroupSizeX_NV = */ 32,
141 /* .maxTaskWorkGroupSizeY_NV = */ 1,
142 /* .maxTaskWorkGroupSizeZ_NV = */ 1,
143 /* .maxMeshViewCountNV = */ 4,
144 /* .maxMeshOutputVerticesEXT = */ 256,
145 /* .maxMeshOutputPrimitivesEXT = */ 512,
146 /* .maxMeshWorkGroupSizeX_EXT = */ 32,
147 /* .maxMeshWorkGroupSizeY_EXT = */ 1,
148 /* .maxMeshWorkGroupSizeZ_EXT = */ 1,
149 /* .maxTaskWorkGroupSizeX_EXT = */ 32,
150 /* .maxTaskWorkGroupSizeY_EXT = */ 1,
151 /* .maxTaskWorkGroupSizeZ_EXT = */ 1,
152 /* .maxMeshViewCountEXT = */ 4,
153 /* .maxDualSourceDrawBuffersEXT = */ 1,
154
155 /* .limits = */ {
156 /* .nonInductiveForLoops = */ 1,
157 /* .whileLoops = */ 1,
158 /* .doWhileLoops = */ 1,
159 /* .generalUniformIndexing = */ 1,
160 /* .generalAttributeMatrixVectorIndexing = */ 1,
161 /* .generalVaryingIndexing = */ 1,
162 /* .generalSamplerIndexing = */ 1,
163 /* .generalVariableIndexing = */ 1,
164 /* .generalConstantMatrixVectorIndexing = */ 1,
165 }};
166
167 static bool initialized = false;
168
169 extern "C" {
170
171 /*
172 * Takes in a GLSL shader as a string and converts it to SPIR-V in binary form.
173 *
174 * |glsl| Null-terminated string containing the shader to be converted.
175 * |stage_int| Magic number indicating the type of shader being processed.
176 * Legal values are as follows:
177 * Vertex = 0
178 * Fragment = 4
179 * Compute = 5
180 * |gen_debug| Flag to indicate if debug information should be generated.
181 * |spirv| Output parameter for a pointer to the resulting SPIR-V data.
182 * |spirv_len| Output parameter for the length of the output binary buffer.
183 *
184 * Returns a void* pointer which, if not null, must be destroyed by
185 * destroy_output_buffer.o. (This is not the same pointer returned in |spirv|.)
186 * If null, the compilation failed.
187 */
188 EMSCRIPTEN_KEEPALIVE
convert_glsl_to_spirv(const char * glsl,int stage_int,bool gen_debug,glslang::EShTargetLanguageVersion spirv_version,uint32_t ** spirv,size_t * spirv_len)189 void* convert_glsl_to_spirv(const char* glsl,
190 int stage_int,
191 bool gen_debug,
192 glslang::EShTargetLanguageVersion spirv_version,
193 uint32_t** spirv,
194 size_t* spirv_len)
195 {
196 if (glsl == nullptr) {
197 fprintf(stderr, "Input pointer null\n");
198 return nullptr;
199 }
200 if (spirv == nullptr || spirv_len == nullptr) {
201 fprintf(stderr, "Output pointer null\n");
202 return nullptr;
203 }
204 *spirv = nullptr;
205 *spirv_len = 0;
206
207 if (stage_int != 0 && stage_int != 4 && stage_int != 5) {
208 fprintf(stderr, "Invalid shader stage\n");
209 return nullptr;
210 }
211 EShLanguage stage = static_cast<EShLanguage>(stage_int);
212 switch (spirv_version) {
213 case glslang::EShTargetSpv_1_0:
214 case glslang::EShTargetSpv_1_1:
215 case glslang::EShTargetSpv_1_2:
216 case glslang::EShTargetSpv_1_3:
217 case glslang::EShTargetSpv_1_4:
218 case glslang::EShTargetSpv_1_5:
219 break;
220 default:
221 fprintf(stderr, "Invalid SPIR-V version number\n");
222 return nullptr;
223 }
224
225 if (!initialized) {
226 glslang::InitializeProcess();
227 initialized = true;
228 }
229
230 glslang::TShader shader(stage);
231 shader.setStrings(&glsl, 1);
232 shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
233 shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
234 shader.setEnvTarget(glslang::EShTargetSpv, spirv_version);
235 if (!shader.parse(&DefaultTBuiltInResource, 100, true, EShMsgDefault)) {
236 fprintf(stderr, "Parse failed\n");
237 fprintf(stderr, "%s\n", shader.getInfoLog());
238 return nullptr;
239 }
240
241 glslang::TProgram program;
242 program.addShader(&shader);
243 if (!program.link(EShMsgDefault)) {
244 fprintf(stderr, "Link failed\n");
245 fprintf(stderr, "%s\n", program.getInfoLog());
246 return nullptr;
247 }
248
249 glslang::SpvOptions spvOptions;
250 spvOptions.generateDebugInfo = gen_debug;
251 spvOptions.optimizeSize = false;
252 spvOptions.disassemble = false;
253 spvOptions.validate = false;
254
255 std::vector<uint32_t>* output = new std::vector<uint32_t>;
256 glslang::GlslangToSpv(*program.getIntermediate(stage), *output, nullptr, &spvOptions);
257
258 *spirv_len = output->size();
259 *spirv = output->data();
260 return output;
261 }
262
263 /*
264 * Destroys a buffer created by convert_glsl_to_spirv
265 */
266 EMSCRIPTEN_KEEPALIVE
destroy_output_buffer(void * p)267 void destroy_output_buffer(void* p)
268 {
269 delete static_cast<std::vector<uint32_t>*>(p);
270 }
271
272 } // extern "C"
273
274 /*
275 * For non-Emscripten builds we supply a generic main, so that the glslang.js
276 * build target can generate an executable with a trivial use case instead of
277 * generating a WASM binary. This is done so that there is a target that can be
278 * built and output analyzed using desktop tools, since WASM binaries are
279 * specific to the Emscripten toolchain.
280 */
281 #ifndef __EMSCRIPTEN__
main()282 int main() {
283 const char* input = R"(#version 310 es
284
285 void main() { })";
286
287 uint32_t* output;
288 size_t output_len;
289
290 void* id = convert_glsl_to_spirv(input, 4, false, glslang::EShTargetSpv_1_0, &output, &output_len);
291 assert(output != nullptr);
292 assert(output_len != 0);
293 destroy_output_buffer(id);
294 return 0;
295 }
296 #endif // ifndef __EMSCRIPTEN__
297