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 /* .maxDualSourceDrawBuffersEXT = */ 1,
145
146 /* .limits = */ {
147 /* .nonInductiveForLoops = */ 1,
148 /* .whileLoops = */ 1,
149 /* .doWhileLoops = */ 1,
150 /* .generalUniformIndexing = */ 1,
151 /* .generalAttributeMatrixVectorIndexing = */ 1,
152 /* .generalVaryingIndexing = */ 1,
153 /* .generalSamplerIndexing = */ 1,
154 /* .generalVariableIndexing = */ 1,
155 /* .generalConstantMatrixVectorIndexing = */ 1,
156 }};
157
158 static bool initialized = false;
159
160 extern "C" {
161
162 /*
163 * Takes in a GLSL shader as a string and converts it to SPIR-V in binary form.
164 *
165 * |glsl| Null-terminated string containing the shader to be converted.
166 * |stage_int| Magic number indicating the type of shader being processed.
167 * Legal values are as follows:
168 * Vertex = 0
169 * Fragment = 4
170 * Compute = 5
171 * |gen_debug| Flag to indicate if debug information should be generated.
172 * |spirv| Output parameter for a pointer to the resulting SPIR-V data.
173 * |spirv_len| Output parameter for the length of the output binary buffer.
174 *
175 * Returns a void* pointer which, if not null, must be destroyed by
176 * destroy_output_buffer.o. (This is not the same pointer returned in |spirv|.)
177 * If null, the compilation failed.
178 */
179 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)180 void* convert_glsl_to_spirv(const char* glsl,
181 int stage_int,
182 bool gen_debug,
183 glslang::EShTargetLanguageVersion spirv_version,
184 uint32_t** spirv,
185 size_t* spirv_len)
186 {
187 if (glsl == nullptr) {
188 fprintf(stderr, "Input pointer null\n");
189 return nullptr;
190 }
191 if (spirv == nullptr || spirv_len == nullptr) {
192 fprintf(stderr, "Output pointer null\n");
193 return nullptr;
194 }
195 *spirv = nullptr;
196 *spirv_len = 0;
197
198 if (stage_int != 0 && stage_int != 4 && stage_int != 5) {
199 fprintf(stderr, "Invalid shader stage\n");
200 return nullptr;
201 }
202 EShLanguage stage = static_cast<EShLanguage>(stage_int);
203 switch (spirv_version) {
204 case glslang::EShTargetSpv_1_0:
205 case glslang::EShTargetSpv_1_1:
206 case glslang::EShTargetSpv_1_2:
207 case glslang::EShTargetSpv_1_3:
208 case glslang::EShTargetSpv_1_4:
209 case glslang::EShTargetSpv_1_5:
210 break;
211 default:
212 fprintf(stderr, "Invalid SPIR-V version number\n");
213 return nullptr;
214 }
215
216 if (!initialized) {
217 glslang::InitializeProcess();
218 initialized = true;
219 }
220
221 glslang::TShader shader(stage);
222 shader.setStrings(&glsl, 1);
223 shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
224 shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
225 shader.setEnvTarget(glslang::EShTargetSpv, spirv_version);
226 if (!shader.parse(&DefaultTBuiltInResource, 100, true, EShMsgDefault)) {
227 fprintf(stderr, "Parse failed\n");
228 fprintf(stderr, "%s\n", shader.getInfoLog());
229 return nullptr;
230 }
231
232 glslang::TProgram program;
233 program.addShader(&shader);
234 if (!program.link(EShMsgDefault)) {
235 fprintf(stderr, "Link failed\n");
236 fprintf(stderr, "%s\n", program.getInfoLog());
237 return nullptr;
238 }
239
240 glslang::SpvOptions spvOptions;
241 spvOptions.generateDebugInfo = gen_debug;
242 spvOptions.optimizeSize = false;
243 spvOptions.disassemble = false;
244 spvOptions.validate = false;
245
246 std::vector<uint32_t>* output = new std::vector<uint32_t>;
247 glslang::GlslangToSpv(*program.getIntermediate(stage), *output, nullptr, &spvOptions);
248
249 *spirv_len = output->size();
250 *spirv = output->data();
251 return output;
252 }
253
254 /*
255 * Destroys a buffer created by convert_glsl_to_spirv
256 */
257 EMSCRIPTEN_KEEPALIVE
destroy_output_buffer(void * p)258 void destroy_output_buffer(void* p)
259 {
260 delete static_cast<std::vector<uint32_t>*>(p);
261 }
262
263 } // extern "C"
264
265 /*
266 * For non-Emscripten builds we supply a generic main, so that the glslang.js
267 * build target can generate an executable with a trivial use case instead of
268 * generating a WASM binary. This is done so that there is a target that can be
269 * built and output analyzed using desktop tools, since WASM binaries are
270 * specific to the Emscripten toolchain.
271 */
272 #ifndef __EMSCRIPTEN__
main()273 int main() {
274 const char* input = R"(#version 310 es
275
276 void main() { })";
277
278 uint32_t* output;
279 size_t output_len;
280
281 void* id = convert_glsl_to_spirv(input, 4, false, glslang::EShTargetSpv_1_0, &output, &output_len);
282 assert(output != nullptr);
283 assert(output_len != 0);
284 destroy_output_buffer(id);
285 return 0;
286 }
287 #endif // ifndef __EMSCRIPTEN__
288