Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
glShader.cpp
Go to the documentation of this file.
1
2
3#include "Headers/glShader.h"
5
10
13
15
16namespace Divide {
17
18namespace
19{
20 constexpr bool g_useSPIRVBinaryCode = false;
21
22 size_t g_validationBufferMaxSize = 4096 * 16;
23
24 FORCE_INLINE gl46core::UseProgramStageMask GetStageMask(const ShaderType type) noexcept
25 {
26 switch (type)
27 {
28 case ShaderType::VERTEX: return gl46core::UseProgramStageMask::GL_VERTEX_SHADER_BIT;
29 case ShaderType::TESSELLATION_CTRL: return gl46core::UseProgramStageMask::GL_TESS_CONTROL_SHADER_BIT;
30 case ShaderType::TESSELLATION_EVAL: return gl46core::UseProgramStageMask::GL_TESS_EVALUATION_SHADER_BIT;
31 case ShaderType::GEOMETRY: return gl46core::UseProgramStageMask::GL_GEOMETRY_SHADER_BIT;
32 case ShaderType::FRAGMENT: return gl46core::UseProgramStageMask::GL_FRAGMENT_SHADER_BIT;
33 case ShaderType::COMPUTE: return gl46core::UseProgramStageMask::GL_COMPUTE_SHADER_BIT;
34 case ShaderType::COUNT: break;
35 }
36
37 return gl46core::UseProgramStageMask::GL_NONE_BIT;
38 }
39
41 {
42 U64 _totalTime{ 0u };
43 U64 _linkTime{ 0u };
44 U64 _linkLogRetrievalTime{ 0u };
45 std::array<U64, to_base(ShaderType::COUNT)> _stageCompileTime{};
46 std::array<U64, to_base(ShaderType::COUNT)> _stageCompileLogRetrievalTime{};
47 };
48}
49
50glShader::glShader(GFXDevice& context, const std::string_view name, const U32 generation)
51 : ShaderModule(context, name, generation)
52{
53}
54
56{
57 if (_handle != GL_NULL_HANDLE)
58 {
59 Console::d_printfn(LOCALE_STR("SHADER_DELETE"), name().c_str());
60 if (!GL_API::DeleteShaderPrograms(1, &_handle))
61 {
63 }
64 }
65}
66
68{
69 if (!_valid)
70 {
71 const auto getTimerAndReset = [](Time::ProfileTimer& timer)
72 {
73 timer.stop();
74 const U64 ret = timer.get();
75 timer.reset();
76 return ret;
77 };
78
79 TimingData timingData{};
80
81 Time::ProfileTimer timers[2];
82
83 Console::d_printfn(LOCALE_STR("GLSL_LOAD_PROGRAM"), _name.c_str(), getGUID());
84
86 {
87 timers[0].start();
88 }
89
90 if (_handle != GL_NULL_HANDLE)
91 {
92 if (!GL_API::DeleteShaderPrograms(1, &_handle))
93 {
95 }
96 }
97
98 _handle = gl46core::glCreateProgram();
99 gl46core::glProgramParameteri(_handle, gl46core::GL_PROGRAM_BINARY_RETRIEVABLE_HINT, gl46core::GL_FALSE);
100 gl46core::glProgramParameteri(_handle, gl46core::GL_PROGRAM_SEPARABLE, gl46core::GL_TRUE);
101
102 if (_handle == 0u || _handle == GL_NULL_HANDLE)
103 {
104 Console::errorfn(LOCALE_STR("ERROR_GLSL_CREATE_PROGRAM"), _name.c_str());
105 _valid = false;
107 }
108
109 bool shouldLink = false;
111 {
112 if (data._type == ShaderType::COUNT)
113 {
114 // stage not specified from the current file. Skip.
115 continue;
116 }
117
118 assert(!data._compiled);
119
121 {
122 timers[1].start();
123 }
124
125 gl46core::GLuint shader = GL_NULL_HANDLE;
126 DIVIDE_ASSERT(shader != 0u && !data._sourceCodeSpirV.empty() && !data._sourceCodeGLSL.empty());
127
128 if constexpr(g_useSPIRVBinaryCode)
129 {
130 shader = gl46core::glCreateShader(GLUtil::glShaderStageTable[to_base(data._type)]);
131 gl46core::glShaderBinary(1, &shader, gl46core::GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, data._sourceCodeSpirV.data(), (gl46core::GLsizei)(data._sourceCodeSpirV.size() * sizeof(SpvWord)));
132 gl46core::glSpecializeShader(shader, "main", 0, nullptr, nullptr);
133 }
134 else
135 {
136 shader = gl46core::glCreateShader(GLUtil::glShaderStageTable[to_base(data._type)]);
137 const char* src = data._sourceCodeGLSL.c_str();
138 gl46core::glShaderSource(shader, 1u, &src, nullptr);
139 gl46core::glCompileShader(shader);
140 }
141
143 {
144 timingData._stageCompileTime[to_base(data._type)] += getTimerAndReset(timers[1]);
145 timers[1].start();
146 }
147
148 if (shader != GL_NULL_HANDLE)
149 {
150 gl46core::GLboolean compiled = 0;
151 gl46core::glGetShaderiv(shader, gl46core::GL_COMPILE_STATUS, &compiled);
152 if (compiled == gl46core::GL_FALSE)
153 {
154 // error
155 gl46core::GLint logSize = 0;
156 gl46core::glGetShaderiv(shader, gl46core::GL_INFO_LOG_LENGTH, &logSize);
157 string validationBuffer;
158 validationBuffer.resize(to_size(logSize) );
159
160 gl46core::glGetShaderInfoLog(shader, logSize, &logSize, &validationBuffer[0]);
161 if (validationBuffer.size() > g_validationBufferMaxSize)
162 {
163 // On some systems, the program's disassembly is printed, and that can get quite large
164 validationBuffer.resize(std::strlen(LOCALE_STR("ERROR_GLSL_COMPILE")) * 2 + g_validationBufferMaxSize);
165 // Use the simple "truncate and inform user" system (a.k.a. add dots and delete the rest)
166 validationBuffer.append(" ... ");
167 }
168
169 Console::errorfn(LOCALE_STR("ERROR_GLSL_COMPILE"), _name.c_str(), shader, Names::shaderTypes[to_base(data._type)], validationBuffer.c_str());
170
171 gl46core::glDeleteShader(shader);
172 }
173 else
174 {
175 _shaderIDs.push_back(shader);
176 gl46core::glAttachShader(_handle, shader);
177 shouldLink = true;
178 data._compiled = true;
179 }
180 }
181
183 {
184 timingData._stageCompileLogRetrievalTime[to_base(data._type)] += getTimerAndReset(timers[1]);
185 }
186 }
187
189 {
190 timers[1].start();
191 }
192
193 if (shouldLink)
194 {
195 assert(!_linked);
196 gl46core::glLinkProgram(_handle);
197 _linked = true;
198 }
199
201 {
202 timingData._linkTime = getTimerAndReset(timers[1]);
203 timers[1].start();
204 }
205
206 // And check the result
207 gl46core::GLboolean linkStatus = gl46core::GL_FALSE;
208 gl46core::glGetProgramiv(_handle, gl46core::GL_LINK_STATUS, &linkStatus);
209
210 // If linking failed, show an error, else print the result in debug builds.
211 if (linkStatus == gl46core::GL_FALSE)
212 {
213 gl46core::GLint logSize = 0;
214 gl46core::glGetProgramiv(_handle, gl46core::GL_INFO_LOG_LENGTH, &logSize);
215 string validationBuffer;
216 validationBuffer.resize(to_size(logSize));
217
218 gl46core::glGetProgramInfoLog(_handle, logSize, nullptr, &validationBuffer[0]);
219 if (validationBuffer.size() > g_validationBufferMaxSize)
220 {
221 // On some systems, the program's disassembly is printed, and that can get quite large
222 validationBuffer.resize(std::strlen(LOCALE_STR("GLSL_LINK_PROGRAM_LOG")) + g_validationBufferMaxSize);
223 // Use the simple "truncate and inform user" system (a.k.a. add dots and delete the rest)
224 validationBuffer.append(" ... ");
225 }
226
227 Console::errorfn(LOCALE_STR("GLSL_LINK_PROGRAM_LOG"), _name.c_str(), validationBuffer.c_str(), getGUID());
229 }
230 else
231 {
233 {
234 Console::printfn(LOCALE_STR("GLSL_LINK_PROGRAM_LOG_OK"), _name.c_str(), "[OK]", getGUID(), _handle);
235 gl46core::glObjectLabel( gl46core::GL_PROGRAM,
236 _handle,
237 -1,
238 _name.c_str() );
239 }
240 _valid = true;
241 }
242
244 {
245 timingData._linkLogRetrievalTime = getTimerAndReset(timers[1]);
246 timingData._totalTime = getTimerAndReset(timers[0]);
247 }
248
249 U8 hotspotIndex = 0u;
250 U64 maxHotspotTimer = 0u;
251 string perStageTiming = "";
252 for (U8 i = 0u; i < to_base(ShaderType::COUNT); ++i)
253 {
254 perStageTiming.append(Util::StringFormat("---- [ {} ] - [{:5.5f} ms] - [{:5.5f} ms]\n",
256 Time::MicrosecondsToMilliseconds<F32>(timingData._stageCompileTime[i]),
257 Time::MicrosecondsToMilliseconds<F32>(timingData._stageCompileLogRetrievalTime[i])));
258 if (timingData._stageCompileTime[i] > maxHotspotTimer)
259 {
260 maxHotspotTimer = timingData._stageCompileTime[i];
261 hotspotIndex = i;
262 }
263
264 if (timingData._stageCompileLogRetrievalTime[i] > maxHotspotTimer)
265 {
266 maxHotspotTimer = timingData._stageCompileLogRetrievalTime[i];
267 hotspotIndex = i;
268 }
269 }
270
271 Console::printfn(LOCALE_STR("SHADER_TIMING_INFO"),
272 name().c_str(),
273 _handle,
274 Time::MicrosecondsToMilliseconds<F32>(timingData._totalTime),
275 Time::MicrosecondsToMilliseconds<F32>(timingData._linkTime),
276 Time::MicrosecondsToMilliseconds<F32>(timingData._linkLogRetrievalTime),
277 Names::shaderTypes[hotspotIndex],
278 perStageTiming.c_str());
279 }
280
281 return _valid ? ShaderResult::OK : ShaderResult::Failed;
282}
283
285{
286 _loadData = data;
287
288 _valid = false; _linked = false;
289
290 if (_handle != GL_NULL_HANDLE)
291 {
292 if (!GL_API::DeleteShaderPrograms(1, &_handle))
293 {
295 }
296 }
297
298 _stageMask = gl46core::UseProgramStageMask::GL_NONE_BIT;
299 for (const ShaderProgram::LoadData& it : _loadData)
300 {
301 if (it._type == ShaderType::COUNT)
302 {
303 continue;
304 }
305
306 assert(!it._sourceCodeGLSL.empty() || !it._sourceCodeSpirV.empty());
307 _stageMask |= GetStageMask(it._type);
308 }
309
310 if (_stageMask == gl46core::UseProgramStageMask::GL_NONE_BIT)
311 {
312 Console::errorfn(LOCALE_STR("ERROR_GLSL_NOT_FOUND"), name().c_str());
313 return false;
314 }
315
316 return true;
317}
318
321 glShaderProgram* program,
322 const std::string_view name,
323 const U32 targetGeneration,
325{
326 glShaderEntry ret
327 {
328 ._fileHash = _ID( name ),
329 ._generation = targetGeneration
330 };
331 {
332 // If we loaded the source code successfully, register it
334 auto& shader_ptr = s_shaderNameMap[ret._fileHash];
335 if (shader_ptr == nullptr || shader_ptr->generation() < ret._generation )
336 {
337 shader_ptr.reset( new glShader( context, name, ret._generation ) );
338
339 // At this stage, we have a valid Shader object, so load the source code
340 if (!static_cast<glShader*>(shader_ptr.get())->load(data))
341 {
343 }
344 }
345 else
346 {
347 Console::d_printfn(LOCALE_STR("SHADER_MANAGER_GET_INC"), shader_ptr->name().c_str());
348 }
349
350 ret._shader = static_cast<glShader*>(shader_ptr.get());
351 }
352
353 ret._shader->registerParent(program);
354 return ret;
355}
356
358{
359 for (auto& shader : _shaderIDs)
360 {
361 if (shader != GL_NULL_HANDLE)
362 {
363 gl46core::glDetachShader(_handle, shader);
364 gl46core::glDeleteShader(shader);
365 }
366 }
367
369}
370
372{
373 if (_pushConstantsLocation == -2)
374 {
375 _pushConstantsLocation = gl46core::glGetUniformLocation( _handle, "PushConstantData" );
376 }
377
378 if ( _pushConstantsLocation > -1 )
379 {
380 gl46core::glProgramUniformMatrix4fv(_handle, _pushConstantsLocation, 2, gl46core::GL_FALSE, pushConstants.dataPtr());
381 }
382}
383} // namespace Divide
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define FORCE_INLINE
Rough around the edges Adapter pattern abstracting the actual rendering API and access to the GPU.
Definition: GFXDevice.h:215
static bool DeleteShaderPrograms(gl46core::GLuint count, gl46core::GLuint *programs)
Definition: GLWrapper.cpp:1829
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
GFXDevice & context() const noexcept
PlatformContext & context() noexcept
void registerParent(ShaderProgram *parent)
static SharedMutex s_shaderNameLock
static ShaderMap s_shaderNameMap
std::array< LoadData, to_base(ShaderType::COUNT)> ShaderLoadData
void uploadPushConstants(const PushConstantsStruct &pushConstants)
Definition: glShader.cpp:371
~glShader() override
Definition: glShader.cpp:55
gl46core::GLint _pushConstantsLocation
Definition: glShader.h:90
vector< gl46core::GLuint > _shaderIDs
Definition: glShader.h:88
static glShaderEntry LoadShader(GFXDevice &context, glShaderProgram *parent, const std::string_view name, U32 targetGeneration, ShaderProgram::ShaderLoadData &data)
Add or refresh a shader from the cache.
Definition: glShader.cpp:320
bool load(const ShaderProgram::ShaderLoadData &data)
Definition: glShader.cpp:284
ShaderProgram::ShaderLoadData _loadData
Definition: glShader.h:87
glShader(GFXDevice &context, const std::string_view name, const U32 generation)
Definition: glShader.cpp:50
void onParentValidation()
Definition: glShader.cpp:357
ShaderResult uploadToGPU()
Definition: glShader.cpp:67
OpenGL implementation of the ShaderProgram entity.
static void Idle(PlatformContext &platformContext)
constexpr bool ENABLE_GPU_VALIDATION
Error callbacks, validations, buffer checks, etc. are controlled by this flag. Heavy performance impa...
Definition: config.h:192
std::array< gl46core::GLenum, to_base(ShaderType::COUNT)> glShaderStageTable
Definition: glResources.cpp:96
static constexpr const char * shaderTypes[]
Str StringFormat(const char *fmt, Args &&...args)
FORCE_INLINE gl46core::UseProgramStageMask GetStageMask(const ShaderType type) noexcept
Definition: glShader.cpp:24
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
uint8_t U8
U32 SpvWord
Definition: ShaderProgram.h:60
constexpr gl46core::GLuint GL_NULL_HANDLE
Invalid object value. Used to compare handles and determine if they were properly created.
Definition: glResources.h:105
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
void efficient_clear(eastl::fixed_vector< T, nodeCount, bEnableOverflow, OverflowAllocator > &fixed_vector)
Definition: Vector.h:52
constexpr size_t to_size(const T value)
ShaderType
Available shader stages.
uint32_t U32
uint64_t U64
constexpr auto to_base(const Type value) -> Type
static NO_INLINE void d_printfn(const char *format, T &&... args)
static NO_INLINE void errorfn(const char *format, T &&... args)
static NO_INLINE void printfn(const char *format, T &&... args)
const F32 * dataPtr() const
Definition: PushConstants.h:51
Definition: glShader.h:49
U64 _fileHash
Definition: glShader.h:51