Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
CompilerLinker.cpp
1//
2// Copyright (C) 2002-2005,2025,2026 3Dlabs Inc. Ltd.
3// Copyright (C) 2013-2016,2025,2026 LunarG, Inc.
4// Copyright (C) 2017-2019,2021,2025,2026 Aeon Games
5//
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions
10// are met:
11//
12// Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14//
15// Redistributions in binary form must reproduce the above
16// copyright notice, this list of conditions and the following
17// disclaimer in the documentation and/or other materials provided
18// with the distribution.
19//
20// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
21// contributors may be used to endorse or promote products derived
22// from this software without specific prior written permission.
23//
24// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35// POSSIBILITY OF SUCH DAMAGE.
36//
37
38// this only applies to the standalone wrapper, not the front end in general
39#ifndef _CRT_SECURE_NO_WARNINGS
40#define _CRT_SECURE_NO_WARNINGS
41#endif
42
43#include "glslang/Include/ResourceLimits.h"
44#include "glslang/Public/ShaderLang.h"
45#include "glslang/SPIRV/GlslangToSpv.h"
46#include "glslang/SPIRV/disassemble.h"
47#include <cstring>
48#include <cstdlib>
49#include <cctype>
50#include <cmath>
51#include <array>
52#include <thread>
53#include "CompilerLinker.hpp"
54
57
58namespace glslang
59{
60 TBuiltInResource DefaultTBuiltInResource;
61}
62
63namespace AeonGames
64{
65 CompilerLinker::CompilerLinker ( TOptions aOptions ) : mOptions ( aOptions )
66 {
67 // Make sure that -E is not specified alongside linking (which includes SPV generation)
68 if ( ( mOptions & EOptionOutputPreprocessed ) && ( mOptions & EOptionLinkProgram ) )
69 {
70 std::cout << LogLevel::Error << "can't use -E when linking is selected" << std::endl;
71 throw std::runtime_error ( "can't use -E when linking is selected" );
72 }
73
74 if ( ( mOptions & EOptionFlattenUniformArrays ) != 0 &&
75 ( mOptions & EOptionReadHlsl ) == 0 )
76 {
77 std::cout << LogLevel::Error << "uniform array flattening only valid when compiling HLSL source." << std::endl;
78 throw std::runtime_error ( "uniform array flattening only valid when compiling HLSL source." );
79 }
80 }
81
82 CompilerLinker::~CompilerLinker()
83 = default;
84
85 void CompilerLinker::AddShaderSource ( EShLanguage aLanguage, const char* aSource )
86 {
87 mShaderCompilationUnits[aLanguage] = aSource;
88 }
89
90 void CompilerLinker::RemoveShaderSource ( EShLanguage aLanguage )
91 {
92 mShaderCompilationUnits[aLanguage] = nullptr;
93 }
94
95 //
96 // Translate the meaningful subset of command-line options to parser-behavior options.
97 //
98 void CompilerLinker::SetMessageOptions ( EShMessages& messages ) const
99 {
100 if ( mOptions & EOptionRelaxedErrors )
101 {
102 messages = ( EShMessages ) ( messages | EShMsgRelaxedErrors );
103 }
104 if ( mOptions & EOptionIntermediate )
105 {
106 messages = ( EShMessages ) ( messages | EShMsgAST );
107 }
108 if ( mOptions & EOptionSuppressWarnings )
109 {
110 messages = ( EShMessages ) ( messages | EShMsgSuppressWarnings );
111 }
112 if ( mOptions & EOptionSpv )
113 {
114 messages = ( EShMessages ) ( messages | EShMsgSpvRules );
115 }
116 if ( mOptions & EOptionVulkanRules )
117 {
118 messages = ( EShMessages ) ( messages | EShMsgVulkanRules );
119 }
120 if ( mOptions & EOptionOutputPreprocessed )
121 {
122 messages = ( EShMessages ) ( messages | EShMsgOnlyPreprocessor );
123 }
124 if ( mOptions & EOptionReadHlsl )
125 {
126 messages = ( EShMessages ) ( messages | EShMsgReadHlsl );
127 }
128 if ( mOptions & EOptionCascadingErrors )
129 {
130 messages = ( EShMessages ) ( messages | EShMsgCascadingErrors );
131 }
132 if ( mOptions & EOptionKeepUncalled )
133 {
134 messages = ( EShMessages ) ( messages | EShMsgKeepUncalled );
135 }
136 }
137
138 //
139 // For linking mode: Will independently parse each compilation unit, but then put them
140 // in the same program and link them together, making at most one linked module per
141 // pipeline stage.
142 //
143 // Uses the new C++ interface instead of the old handle-based interface.
144 //
146 {
147 bool compile_failed = false;
148 bool link_failed = false;
149 mLog.clear();
150 /* The original code used heap memory for program and shaders
151 in order to destroy the program first and the shaders last,
152 that shouldn't be necessary with this new approach,
153 keep the order of declaration of the following two locals
154 to ensure program is destroyed before shaders. */
155 std::vector<glslang::TShader> shaders;
156 glslang::TProgram program;
157 EShMessages messages = EShMsgDefault;
158 SetMessageOptions ( messages );
159 //
160 // Per-shader processing...
161 //
162 shaders.reserve ( mShaderCompilationUnits.size() );
163 for ( size_t i = 0; i < mShaderCompilationUnits.size(); ++i )
164 {
165 if ( !mShaderCompilationUnits[i] )
166 {
167 continue;
168 }
169 shaders.emplace_back ( static_cast<EShLanguage> ( i ) );
170 std::array<const char*, 1> source{ {mShaderCompilationUnits[i]} };
171 auto stage_name = glslang::StageName ( static_cast<EShLanguage> ( i ) );
172 shaders.back().setStringsWithLengthsAndNames ( source.data(), nullptr, &stage_name, 1 );
173 shaders.back().setShiftSamplerBinding ( mBaseSamplerBinding[i] );
174 shaders.back().setShiftTextureBinding ( mBaseTextureBinding[i] );
175 shaders.back().setShiftImageBinding ( mBaseImageBinding[i] );
176 shaders.back().setShiftUboBinding ( mBaseUboBinding[i] );
177#ifdef ENABLE_HLSL
178 shaders.back().setFlattenUniformArrays ( ( mOptions & EOptionFlattenUniformArrays ) != 0 );
179#endif
180 shaders.back().setNoStorageFormat ( ( mOptions & EOptionNoStorageFormat ) != 0 );
181 if ( mOptions & EOptionAutoMapBindings )
182 {
183 shaders.back().setAutoMapBindings ( true );
184 }
185 const int defaultVersion = mOptions & EOptionDefaultDesktop ? 110 : 100;
186 glslang::DefaultTBuiltInResource.maxDrawBuffers = true;
187 if ( !shaders.back().parse ( &glslang::DefaultTBuiltInResource, defaultVersion, false, messages ) )
188 {
189 compile_failed = true;
190 }
191 }
192 /* We have to add the shaders
193 AFTER we know the shaders array
194 is no longer going be shifted around,
195 IE: When we're not going to be adding any more to it. */
196 for ( auto& i : shaders )
197 {
198 program.addShader ( &i );
199 }
200 //
201 // Program-level processing...
202 //
203 // Link
204 // Map IO
205 if ( ( ! ( mOptions & EOptionOutputPreprocessed ) && !program.link ( messages ) ) ||
206 ( ( mOptions & EOptionSpv ) && ( !program.mapIO() ) ) )
207 {
208 link_failed = true;
209 }
210
211 // Reflect
212 if ( mOptions & EOptionDumpReflection )
213 {
214 program.buildReflection();
215 program.dumpReflection();
216 }
217
218 // Dump SPIR-V
219 if ( mOptions & EOptionSpv )
220 {
221 if ( compile_failed || link_failed )
222 {
223 for ( auto& i : shaders )
224 {
225 mLog.append ( i.getInfoLog() );
226 mLog.append ( i.getInfoDebugLog() );
227 }
228 mLog.append ( program.getInfoLog() );
229 mLog.append ( program.getInfoDebugLog() );
230 }
231 else
232 {
233 for ( int stage = 0; stage < EShLangCount; ++stage )
234 {
235 if ( program.getIntermediate ( ( EShLanguage ) stage ) )
236 {
237 mSpirV[stage].clear();
238 std::string warningsErrors;
239 spv::SpvBuildLogger logger;
240 glslang::GlslangToSpv ( *program.getIntermediate ( ( EShLanguage ) stage ), mSpirV[stage], &logger );
241 }
242 }
243 }
244 }
245 if ( compile_failed )
246 {
247 return EFailCompile;
248 }
249 else if ( link_failed )
250 {
251 return EFailLink;
252 }
253 return ESuccess;
254 }
255
256 const std::vector<uint32_t>& CompilerLinker::GetSpirV ( EShLanguage aStage ) const
257 {
258 return mSpirV[aStage];
259 }
260
261 const std::string & CompilerLinker::GetLog() const
262 {
263 return mLog;
264 }
265}
Defines log severity levels and stream output for the AeonGames engine.
Platform-specific macros, includes, and DLL export/import definitions.
FailCode
Process exit/result codes.
FailCode CompileAndLink()
Compile all added shader sources and link into a program.
CompilerLinker(TOptions aOptions=static_cast< TOptions >(EOptionSpv|EOptionVulkanRules|EOptionLinkProgram))
Constructor.
const std::vector< uint32_t > & GetSpirV(EShLanguage aStage) const
Get the compiled SPIR-V bytecode for a shader stage.
void AddShaderSource(EShLanguage aStage, const char *aSource)
Add GLSL source code for a shader stage.
void RemoveShaderSource(EShLanguage aStage)
Remove the shader source for a given stage.
const std::string & GetLog() const
Get the compilation/link log output.
TOptions
Compiler/linker option flags.
<- This is here just for the literals
Definition AABB.hpp:31
@ Error
Error conditions.
Definition LogLevel.hpp:33