Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
glShaderProgram.cpp
Go to the documentation of this file.
1
2
4#include "Headers/glShader.h"
5
11
12namespace Divide {
13
14namespace
15{
16 constexpr size_t g_validationBufferMaxSize = 64 * 1024;
17 NO_DESTROY moodycamel::BlockingConcurrentQueue<ValidationEntry> g_sValidationQueue;
18
20 NO_DESTROY eastl::set<gl46core::GLuint> g_deletionSet;
21}
22
23void glShaderProgram::Idle( [[maybe_unused]] PlatformContext& platformContext )
24{
25 NO_DESTROY thread_local ValidationEntry s_validationOutputCache;
26
28
29 if (!g_sValidationQueue.try_dequeue(s_validationOutputCache))
30 {
31 return;
32 }
33 {
34 SharedLock<SharedMutex> w_lock(g_deletionSetLock);
35 if (g_deletionSet.find(s_validationOutputCache._handle) != std::cend(g_deletionSet))
36 {
37 return;
38 }
39 }
40
41 assert(s_validationOutputCache._handle != GL_NULL_HANDLE);
42
43 gl46core::glValidateProgramPipeline(s_validationOutputCache._handle);
44
45 gl46core::GLint status = 1;
46 if (s_validationOutputCache._stageMask != gl46core::UseProgramStageMask::GL_COMPUTE_SHADER_BIT)
47 {
48 gl46core::glGetProgramPipelineiv(s_validationOutputCache._handle, gl46core::GL_VALIDATE_STATUS, &status);
49 }
50
51 // we print errors in debug and in release, but everything else only in debug
52 // the validation log is only retrieved if we request it. (i.e. in release,
53 // if the shader is validated, it isn't retrieved)
54 if (status == 0)
55 {
56 // Query the size of the log
57 gl46core::GLint length = 0;
58 gl46core::glGetProgramPipelineiv(s_validationOutputCache._handle, gl46core::GL_INFO_LOG_LENGTH, &length);
59 // If we actually have something in the validation log
60 if (length > 1)
61 {
62 string validationBuffer;
63 validationBuffer.resize(length);
64 gl46core::glGetProgramPipelineInfoLog(s_validationOutputCache._handle, length, nullptr, &validationBuffer[0]);
65
66 // To avoid overflowing the output buffers (both CEGUI and Console), limit the maximum output size
67 if (validationBuffer.size() > g_validationBufferMaxSize)
68 {
69 // On some systems, the program's disassembly is printed, and that can get quite large
70 validationBuffer.resize(std::strlen(LOCALE_STR("GLSL_LINK_PROGRAM_LOG")) + g_validationBufferMaxSize);
71 // Use the simple "truncate and inform user" system (a.k.a. add dots and delete the rest)
72 validationBuffer.append(" ... ");
73 }
74 // Return the final message, whatever it may contain
75 Console::errorfn(LOCALE_STR("GLSL_VALIDATING_PROGRAM"), s_validationOutputCache._handle, s_validationOutputCache._name.c_str(), validationBuffer.c_str());
76 }
77 else
78 {
79 Console::errorfn(LOCALE_STR("GLSL_VALIDATING_PROGRAM"), s_validationOutputCache._handle, s_validationOutputCache._name.c_str(), "[ Couldn't retrieve info log! ]");
80 }
81 }
82 else
83 {
84 Console::d_printfn(LOCALE_STR("GLSL_VALIDATING_PROGRAM"), s_validationOutputCache._handle, s_validationOutputCache._name.c_str(), "[ OK! ]");
85 }
86}
87
89 : ShaderProgram(context, descriptor)
90{
91}
92
94{
95}
96
98{
100 {
101 {
102 LockGuard<SharedMutex> w_lock(g_deletionSetLock);
103 g_deletionSet.insert(_glHandle);
104 }
105
106 if (GL_API::GetStateTracker()._activeShaderPipelineHandle == _glHandle)
107 {
108 if (GL_API::GetStateTracker().setActiveShaderPipeline(0u) == GLStateTracker::BindResult::FAILED)
109 {
111 }
112 }
113
114 gl46core::glDeleteProgramPipelines(1, &_glHandle);
116 }
117
118 for ( glShaderEntry& shader : _shaderStage )
119 {
120 shader._shader->deregisterParent( this );
121 }
122
123 _shaderStage.clear();
124
125 return ShaderProgram::unload();
126}
127
129{
131
133 {
134 return;
135 }
136
137 _validationQueued = false;
138
139 gl46core::UseProgramStageMask stageMask = gl46core::UseProgramStageMask::GL_NONE_BIT;
140 for ( glShaderEntry& shader : _shaderStage)
141 {
142 if (!shader._shader->valid())
143 {
144 continue;
145 }
146
147 shader._shader->onParentValidation();
148 stageMask |= shader._shader->stageMask();
149 }
150
152 {
153 g_sValidationQueue.enqueue({ resourceName(), _glHandle, stageMask});
154 }
155}
156
158{
161 if ( ret == ShaderResult::OK)
162 {
163 if (!_stagesBound && rebind)
164 {
167 {
168 gl46core::glCreateProgramPipelines(1, &_glHandle);
170 {
171 gl46core::glObjectLabel( gl46core::GL_PROGRAM_PIPELINE,
172 _glHandle,
173 -1,
174 resourceName().c_str() );
175 }
176 // We can reuse previous handles
177 LockGuard<SharedMutex> w_lock(g_deletionSetLock);
178 g_deletionSet.erase(_glHandle);
179 }
180
181 if (rebind)
182 {
183 assert(_glHandle != GL_NULL_HANDLE);
184
185 for ( glShaderEntry& shader : _shaderStage)
186 {
187 ret = shader._shader->uploadToGPU();
188 if (ret != ShaderResult::OK) {
189 _stagesBound = true;
190 break;
191 }
192
193 // If a shader exists for said stage, attach it
194 gl46core::glUseProgramStages(_glHandle, shader._shader->stageMask(), shader._shader->handle());
195 }
196
197 if (ret == ShaderResult::OK)
198 {
199 _validationQueued = true;
200 _stagesBound = true;
201 }
202 }
203 }
204 }
205
206 return ret;
207}
208
210{
212
213 if (ShaderProgram::loadInternal(fileData, overwrite))
214 {
215 _stagesBound = false;
216
217 for (auto& [fileHash, loadDataPerFile] : fileData)
218 {
219 assert(!loadDataPerFile._modules.empty());
220
221 bool found = false;
222 U32 targetGeneration = 0u;
223 for ( glShaderEntry& stage : _shaderStage )
224 {
225 if ( stage._fileHash == _ID ( loadDataPerFile._programName.c_str()) )
226 {
227 targetGeneration = overwrite ? stage._generation + 1u : stage._generation;
228 stage = glShader::LoadShader( _context, this, loadDataPerFile._programName.c_str(), targetGeneration, loadDataPerFile._loadData );
229 found = true;
230 break;
231 }
232 }
233 if (!found )
234 {
235 _shaderStage.push_back( glShader::LoadShader(_context, this, loadDataPerFile._programName.c_str(), targetGeneration, loadDataPerFile._loadData) );
236 }
237 }
238
239 return !_shaderStage.empty();
240 }
241
242 return false;
243}
244
247{
249
250 // If the shader isn't ready or failed to link, stop here
251 const ShaderResult ret = validatePreBind(true);
252 if (ret != ShaderResult::OK)
253 {
254 return ret;
255 }
256
257 // Set this program as the currently active one
259 {
260 // All of this needs to be run on an actual bind operation. If we are already bound, we assume we did all this
262 }
263
264 return ShaderResult::OK;
265}
266
268{
269 if (pushConstants.set())
270 {
271 for ( glShaderEntry& shader : _shaderStage)
272 {
273 if (!shader._shader->valid())
274 {
275 continue;
276 }
277
278 shader._shader->uploadPushConstants(pushConstants);
279 }
280 }
281}
282};
#define LOCALE_STR(X)
Definition: Localization.h:91
#define NO_DESTROY
#define DIVIDE_UNEXPECTED_CALL()
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
static GLStateTracker & GetStateTracker() noexcept
Definition: GLWrapper.cpp:1749
ResourceState getState() const noexcept
Definition: Resource.cpp:17
virtual ShaderResult validatePreBind(bool rebind=true)
virtual bool loadInternal(hashMap< U64, PerFileShaderData > &fileData, bool overwrite)
bool unload() override
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
ShaderResult validatePreBind(bool rebind=true) override
void uploadPushConstants(const PushConstantsStruct &pushConstants)
bool unload() override
Make sure this program is ready for deletion.
bool loadInternal(hashMap< U64, PerFileShaderData > &fileData, bool overwrite) override
Returns true if at least one shader linked successfully.
glShaderProgram(PlatformContext &context, const ResourceDescriptor< ShaderProgram > &descriptor)
gl46core::GLuint _glHandle
static void Idle(PlatformContext &platformContext)
ShaderResult bind()
Bind this shader program (returns false if the program failed validation)
constexpr bool ENABLE_GPU_VALIDATION
Error callbacks, validations, buffer checks, etc. are controlled by this flag. Heavy performance impa...
Definition: config.h:192
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
NO_DESTROY eastl::set< gl46core::GLuint > g_deletionSet
NO_DESTROY moodycamel::BlockingConcurrentQueue< ValidationEntry > g_sValidationQueue
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
@ RES_LOADED
The resource is available for usage.
std::shared_mutex SharedMutex
Definition: SharedMutex.h:43
constexpr gl46core::GLuint GL_NULL_HANDLE
Invalid object value. Used to compare handles and determine if they were properly created.
Definition: glResources.h:105
hashAlg::unordered_map< K, V, HashFun, Predicate > hashMap
Definition: HashMap.h:55
std::shared_lock< mutex > SharedLock
Definition: SharedMutex.h:49
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
uint32_t U32
static NO_INLINE void d_printfn(const char *format, T &&... args)
static NO_INLINE void errorfn(const char *format, T &&... args)
bool set() const noexcept
Definition: PushConstants.h:44
Definition: glShader.h:49