neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
shader.hpp
Go to the documentation of this file.
1// shader.hpp
2/*
3 neogfx C++ App/Game Engine
4 Copyright (c) 2019, 2020 Leigh Johnston. All Rights Reserved.
5
6 This program is free software: you can redistribute it and / or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include <neogfx/neogfx.hpp>
23#include <neolib/core/jar.hpp>
24#include <neolib/core/set.hpp>
27#include "shader.glsl.hpp"
28#include "shader.glsl-gles.hpp"
29
30namespace neogfx
31{
32 #define cache_uniform( uniformName ) cached_uniform uniformName = { *this, #uniformName };
33 #define cache_shared_uniform( uniformName ) cached_uniform uniformName = { *this, #uniformName, true };
34
35 template <typename Base>
36 class shader : public reference_counted<Base>
37 {
38 typedef shader<Base> self_type;
40 public:
41 using typename base_type::abstract_type;
42 typedef i_shader::value_type abstract_value_type;
43 typedef shader_value_type value_type;
44 protected:
48 {
49 public:
50 cached_uniform(shader<Base>& aParent, const char* const aName, bool aShared = false) :
51 iParent{ aParent }, iName{ aName }, iShared{ aShared }
52 {
53 }
54 public:
55 const i_shader_uniform& uniform() const
56 {
57 if (!iId)
58 {
59 auto existing = iParent.find_uniform(iName);
60 if (existing != no_uniform)
61 iId = existing;
62 if (iId == std::nullopt)
63 iId = iParent.create_uniform(iName, iShared);
64 }
65 return iParent.uniforms()[*iId];
66 }
67 i_shader_uniform& uniform()
68 {
69 return const_cast<i_shader_uniform&>(to_const(*this).uniform());
70 }
71 template <typename T>
72 i_shader_uniform& operator=(const T& aValue)
73 {
74 uniform().set_value(aValue);
75 return uniform();
76 }
77 private:
78 shader<Base>& iParent;
79 string iName;
80 bool iShared;
81 mutable cache<shader_uniform_id> iId;
82 };
83 public:
84 shader(shader_type aType, std::string const& aName, bool aEnabled = true) :
85 iType{ aType },
86 iName{ aName },
87 iEnabled{ aEnabled },
88 iDirty{ true },
89 iHasSharedUniforms{ false }
90 {
91 }
93 {
94 if (iHandle)
95 service<i_rendering_engine>().destroy_shader_object(*iHandle);
96 }
97 public:
98 shader_type type() const final
99 {
100 return iType;
101 }
102 const i_string& name() const final
103 {
104 return iName;
105 }
106 bool supports(vertex_buffer_type aBufferType) const override
107 {
108 return false;
109 }
110 void* handle(const i_shader_program& aProgram) const final
111 {
112 if (!aProgram.is_first_in_stage(*this))
113 return aProgram.first_in_stage(type()).handle(aProgram);
114 if (!iHandle)
115 iHandle = service<i_rendering_engine>().create_shader_object(type());
116 if (*iHandle == nullptr)
117 throw failed_to_create_shader_program("Failed to create shader object");
118 return *iHandle;
119 }
120 bool enabled() const final
121 {
122 return iEnabled;
123 }
124 bool disabled() const final
125 {
126 return !iEnabled;
127 }
128 void enable() final
129 {
130 if (!iEnabled)
131 {
132 iEnabled = true;
133 set_dirty();
134 }
135 }
136 void disable() final
137 {
138 if (iEnabled)
139 {
140 iEnabled = false;
141 set_dirty();
142 }
143 }
144 bool dirty() const final
145 {
146 return iDirty;
147 }
148 void set_dirty() final
149 {
150 iDirty = true;
151 for (auto& u : uniforms())
152 u.clear_storage();
153 }
154 void set_clean() final
155 {
156 iDirty = false;
157 }
158 bool uniforms_changed() const final
159 {
160 for (auto& u : uniforms())
161 if (u.is_dirty())
162 return true;
163 return false;
164 }
165 public:
166 const i_shader::uniform_list& uniforms() const final
167 {
168 return iUniforms.items();
169 }
170 bool has_shared_uniforms() const final
171 {
172 return iHasSharedUniforms;
173 }
174 protected:
176 {
177 return iUniforms;
178 }
179 public:
180 void clear_uniform(shader_uniform_id aUniform) final
181 {
182 iUniforms.remove(aUniform);
183 set_dirty();
184 }
185 shader_uniform_id create_uniform(const i_string& aName, bool aShared = false) final
186 {
187 auto id = uniforms().next_cookie();
188 uniforms().add(id, id, aName, aShared, value_type{});
189 iHasSharedUniforms = iHasSharedUniforms || aShared;
190 set_dirty();
191 return id;
192 }
193 shader_uniform_id find_uniform(const i_string& aName) const final
194 {
195 for (auto const& u : uniforms())
196 if (u.name() == aName)
197 return u.id();
198 return no_uniform;
199 }
200 using i_shader::set_uniform;
201 void set_uniform(shader_uniform_id aUniform, const abstract_value_type& aValue) final
202 {
203 auto& u = uniforms()[aUniform];
204 if (u.value().empty() != aValue.empty() || (!u.value().empty() && u.different_type_to(aValue)))
205 set_dirty();
206 u.set_value(aValue);
207 }
208 void clear_uniform_storage(shader_uniform_id aUniform) final
209 {
210 uniforms()[aUniform].clear_storage();
211 }
212 void update_uniform_storage(shader_uniform_id aUniform, shader_uniform_storage aStorage) final
213 {
214 uniforms()[aUniform].set_storage(aStorage);
215 }
216 void clear_uniform_location(shader_uniform_id aUniform) final
217 {
218 uniforms()[aUniform].clear_location();
219 }
220 void update_uniform_location(shader_uniform_id aUniform, shader_uniform_location aLocation) final
221 {
222 uniforms()[aUniform].set_location(aLocation);
223 }
224 const variable_list& in_variables() const final
225 {
226 return iInVariables;
227 }
228 const variable_list& out_variables() const final
229 {
230 return iOutVariables;
231 }
232 void clear_variable(const i_string& aName) final
233 {
234 for (auto v = in_variables().begin(); v != in_variables().end(); ++v)
235 if (v->name() == aName)
236 {
237 in_variables().erase(v);
238 return;
239 }
240 for (auto v = out_variables().begin(); v != out_variables().end(); ++v)
241 if (v->name() == aName)
242 {
243 out_variables().erase(v);
244 return;
245 }
246 }
247 i_shader_variable& add_variable(const i_shader_variable& aVariable) final
248 {
249 auto& variableList = ((aVariable.qualifier().value<shader_variable_qualifier>() & shader_variable_qualifier::In) == shader_variable_qualifier::In ?
250 iInVariables : iOutVariables);
251 auto existing = variableList.find(aVariable);
252 if (existing == variableList.end())
253 {
254 auto& v = *variableList.insert(aVariable);
255 set_dirty();
256 return v;
257 }
258 if (existing->name() != aVariable.name())
259 set_dirty();
260 *existing = aVariable;
261 return *existing;
262 }
263 public:
265 {
266 }
267 void generate_code(const i_shader_program& aProgram, shader_language aLanguage, i_string& aOutput) const override
268 {
269 if (aProgram.is_first_in_stage(*this))
270 {
271 if (aLanguage == shader_language::Glsl)
272 {
273 switch (service<i_rendering_engine>().renderer())
274 {
275 case renderer::OpenGL:
277 default:
278 aOutput = glsl::Shader;
279 break;
281 aOutput = glsl::gles::Shader;
282 break;
283 }
284 }
285 else
286 throw unsupported_shader_language();
287 }
288 if (aProgram.is_last_in_stage(*this))
289 {
290 if (aLanguage == shader_language::Glsl)
291 {
292 std::map<string, string> uniformDefinitions;
293 std::map<string, string> singularUniformDefinitions;
294 std::map<std::pair<shader_variable_qualifier, shader_variable_location>, string> variableDefinitions;
295 for (auto const& s : aProgram.stage(type())->shaders())
296 {
297 for (auto const& u : s->uniforms())
298 {
299 if (s->disabled() && !u.shared())
300 continue;
301 string uniformDefinition;
302 switch (u.value().which())
303 {
305 uniformDefinition = " float %I%["_s + to_string(u.value().get<abstract_t<shader_float_array>>().size()) +"];\n"_s;
306 break;
308 uniformDefinition = " double %I%["_s + to_string(u.value().get<abstract_t<shader_double_array>>().size()) +"];\n"_s;
309 break;
310 default:
311 if (!u.singular())
312 uniformDefinition = " %T% %I%;\n"_s;
313 else
314 uniformDefinition = "uniform %T% %I%;\n"_s;
315 break;
316 }
317 uniformDefinition.replace_all("%T%"_s, enum_to_string(u.value().which()));
318 uniformDefinition.replace_all("%I%"_s, u.name());
319 if (!u.singular())
320 uniformDefinitions[u.name()] = uniformDefinition;
321 else
322 singularUniformDefinitions[u.name()] = uniformDefinition;
323 }
324 if (s->disabled())
325 continue;
326 for (auto const& v : s->in_variables())
327 {
328 string variableDefinition = "layout (location = %L%) %I% in %T% %N%;\n"_s;
329 variableDefinition.replace_all("%I%"_s,
331 "flat"_s : ""_s);
332 variableDefinition.replace_all("%T%"_s, enum_to_string<shader_data_type>(v.type()));
333 variableDefinition.replace_all("%L%"_s, to_string(v.location()));
334 variableDefinition.replace_all("%N%"_s, v.name());
335 variableDefinitions[std::make_pair(v.qualifier().value<shader_variable_qualifier>(), v.location())] = variableDefinition;
336 };
337 for (auto const& v : s->out_variables())
338 {
339 string variableDefinition = "layout (location = %L%) %I% out %T% %N%;\n"_s;
340 variableDefinition.replace_all("%I%"_s,
342 "flat"_s : ""_s);
343 variableDefinition.replace_all("%T%"_s, enum_to_string<shader_data_type>(v.type()));
344 variableDefinition.replace_all("%L%"_s, to_string(v.location()));
345 variableDefinition.replace_all("%N%"_s, v.name());
346 variableDefinitions[std::make_pair(v.qualifier().value<shader_variable_qualifier>(), v.location())] = variableDefinition;
347 };
348 }
349 string udefs;
350 for (auto const& udef : uniformDefinitions)
351 udefs += udef.second;
352 string sudefs;
353 for (auto const& sudef : singularUniformDefinitions)
354 sudefs += sudef.second;
355 string vdefs;
356 for (auto const& vdef : variableDefinitions)
357 vdefs += vdef.second;
358 aOutput.replace_all("%UNIFORM_BLOCK_INDEX%"_s, neolib::string{ std::to_string(static_cast<std::uint32_t>(type())) });
359 aOutput.replace_all("%UNIFORM_BLOCK_NAME%"_s, enum_to_string(type()) + "Uniforms");
360 aOutput.replace_all("%UNIFORMS%"_s, udefs);
361 aOutput.replace_all("%SINGULAR_UNIFORMS%"_s, sudefs);
362 aOutput.replace_all("%VARIABLES%"_s, vdefs);
363 }
364 else
365 throw unsupported_shader_language();
366 }
367 }
368 void generate_invoke(const i_shader_program& aProgram, shader_language aLanguage, i_string& aInvokes) const final
369 {
370 aInvokes += " "_s + name() + "("_s;
371 bool first = true;
372 for (auto const& out : out_variables())
373 {
374 if (!first)
375 aInvokes += ", "_s;
376 else
377 first = false;
378 aInvokes += ("arg"_s + out.name());
379 }
380 aInvokes += ");\n"_s;
381 }
382 protected:
384 {
385 return iInVariables;
386 }
388 {
389 return iOutVariables;
390 }
391 private:
392 shader_type iType;
393 string iName;
394 mutable cache<void*> iHandle;
395 bool iEnabled;
396 bool iDirty;
397 uniform_list iUniforms;
398 bool iHasSharedUniforms;
399 variable_list iInVariables;
400 variable_list iOutVariables;
401 };
402}
virtual bool is_last_in_stage(const i_shader &aShader) const =0
virtual bool is_first_in_stage(const i_shader &aShader) const =0
virtual const i_stage_t & stage(shader_type aStage) const =0
const i_shader_uniform & uniform() const
Definition shader.hpp:55
cached_uniform(shader< Base > &aParent, const char *const aName, bool aShared=false)
Definition shader.hpp:50
i_shader_uniform & uniform()
Definition shader.hpp:67
i_shader_uniform & operator=(const T &aValue)
Definition shader.hpp:72
void disable() final
Definition shader.hpp:136
shader_uniform_id find_uniform(const i_string &aName) const final
Definition shader.hpp:193
void set_clean() final
Definition shader.hpp:154
void generate_invoke(const i_shader_program &aProgram, shader_language aLanguage, i_string &aInvokes) const final
Definition shader.hpp:368
bool disabled() const final
Definition shader.hpp:124
shader_value_type value_type
Definition shader.hpp:43
neolib::jar< shader_uniform > uniform_list
Definition shader.hpp:45
void update_uniform_location(shader_uniform_id aUniform, shader_uniform_location aLocation) final
Definition shader.hpp:220
shader_type type() const final
Definition shader.hpp:98
const i_shader::uniform_list & uniforms() const final
Definition shader.hpp:166
i_shader::value_type abstract_value_type
Definition shader.hpp:42
void prepare_uniforms(const i_rendering_context &, i_shader_program &) override
Definition shader.hpp:264
void * handle(const i_shader_program &aProgram) const final
Definition shader.hpp:110
bool dirty() const final
Definition shader.hpp:144
bool supports(vertex_buffer_type aBufferType) const override
Definition shader.hpp:106
void clear_uniform(shader_uniform_id aUniform) final
Definition shader.hpp:180
void update_uniform_storage(shader_uniform_id aUniform, shader_uniform_storage aStorage) final
Definition shader.hpp:212
void set_dirty() final
Definition shader.hpp:148
bool enabled() const final
Definition shader.hpp:120
void clear_uniform_location(shader_uniform_id aUniform) final
Definition shader.hpp:216
shader(shader_type aType, std::string const &aName, bool aEnabled=true)
Definition shader.hpp:84
variable_list & in_variables()
Definition shader.hpp:383
const variable_list & out_variables() const final
Definition shader.hpp:228
i_shader_variable & add_variable(const i_shader_variable &aVariable) final
Definition shader.hpp:247
const i_string & name() const final
Definition shader.hpp:102
void set_uniform(shader_uniform_id aUniform, const abstract_value_type &aValue) final
Definition shader.hpp:201
void clear_variable(const i_string &aName) final
Definition shader.hpp:232
void clear_uniform_storage(shader_uniform_id aUniform) final
Definition shader.hpp:208
neolib::set< shader_variable > variable_list
Definition shader.hpp:46
void generate_code(const i_shader_program &aProgram, shader_language aLanguage, i_string &aOutput) const override
Definition shader.hpp:267
void enable() final
Definition shader.hpp:128
const variable_list & in_variables() const final
Definition shader.hpp:224
variable_list & out_variables()
Definition shader.hpp:387
bool uniforms_changed() const final
Definition shader.hpp:158
bool has_shared_uniforms() const final
Definition shader.hpp:170
shader_uniform_id create_uniform(const i_string &aName, bool aShared=false) final
Definition shader.hpp:185
uniform_list & uniforms()
Definition shader.hpp:175
virtual void replace_all(const i_string &aSearch, const i_string &aReplace)=0
void replace_all(const i_string &aSearch, const i_string &aReplace) final
Definition string.hpp:137
uint32_t id
shader_variable_qualifier
Definition i_shader.hpp:47
std::string to_string(note const &aNote)
shader_language
Definition i_shader.hpp:64
basic_jar< T, vector< T >, cookie, MutexType > jar
Definition jar.hpp:736