neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
i_shader.hpp
Go to the documentation of this file.
1// i_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>
24#include <neolib/core/jar.hpp>
27#include <neolib/core/i_set.hpp>
32
33namespace neogfx
34{
35 enum class shader_type : uint32_t
36 {
37 Compute = 0,
38 Vertex = 1,
41 Geometry = 4,
42 Fragment = 5,
43 COUNT
44 };
45
46 enum class shader_variable_qualifier : uint32_t
47 {
48 In = 0x00000001,
49 Out = 0x00000002,
50 Flat = 0x00000010
51 };
52
54 {
55 return static_cast<shader_variable_qualifier>(static_cast<uint32_t>(aLhs) | static_cast<uint32_t>(aRhs));
56 }
57
59 {
60 return static_cast<shader_variable_qualifier>(static_cast<uint32_t>(aLhs) & static_cast<uint32_t>(aRhs));
61 }
62
63 enum class shader_language : uint32_t
64 {
65 Glsl
66 };
67
68 enum class shader_data_type : uint32_t
69 {
70 Boolean,
71 Float,
72 Double,
73 Int,
74 Uint,
75 Vec2,
76 DVec2,
77 IVec2,
78 UVec2,
79 Vec3,
80 DVec3,
81 IVec3,
82 UVec3,
83 Vec4,
84 DVec4,
85 IVec4,
86 UVec4,
87 Mat4,
88 DMat4,
94 };
95}
96
100declare_enum_string(neogfx::shader_type, TessellationControl)
101declare_enum_string(neogfx::shader_type, TessellationEvaluation)
105
111
115
140declare_enum_string_explicit(neogfx::shader_data_type, Sampler2DRect, sampler2DRect)
142
143namespace neogfx
144{
145 struct unknown_uniform_storage : std::logic_error { unknown_uniform_storage() : std::logic_error{ "neogfx::unknown_uniform_storage" } {} };
146 struct unknown_uniform_location : std::logic_error { unknown_uniform_location() : std::logic_error{ "neogfx::unknown_uniform_location" } {} };
147 struct shader_variable_not_linked : std::logic_error { shader_variable_not_linked() : std::logic_error{ "neogfx::shader_variable_not_linked" } {} };
148 struct shader_variable_not_found : std::logic_error { shader_variable_not_found() : std::logic_error{ "neogfx::shader_variable_not_found" } {} };
149 struct invalid_shader_variable_type : std::logic_error { invalid_shader_variable_type() : std::logic_error{ "neogfx::invalid_shader_variable_type" } {} };
150 struct unsupported_shader_language : std::logic_error { unsupported_shader_language() : std::logic_error{ "neogfx::unsupported_shader_language" } {} };
151
152 typedef neolib::vector<float> shader_float_array;
153 typedef neolib::vector<double> shader_double_array;
154
155 template <shader_data_type HandleType>
156 struct shader_handle
157 {
158 int handle;
159 typedef shader_handle<HandleType> self_type;
160 typedef self_type abstract_type;
161 bool operator==(const self_type& aRhs) const { return handle == aRhs.handle; }
162 };
163
164 typedef shader_handle<shader_data_type::Sampler2D> sampler2D;
165 typedef shader_handle<shader_data_type::Sampler2DMS> sampler2DMS;
166 typedef shader_handle<shader_data_type::Sampler2DRect> sampler2DRect;
167
169
170 typedef uint32_t shader_variable_location;
171 typedef void* shader_uniform_storage;
172 typedef int32_t shader_uniform_location;
173
174 typedef neolib::cookie shader_uniform_id;
175 constexpr shader_uniform_id no_uniform = shader_uniform_id{};
176
177 class i_shader_uniform
178 {
179 public:
180 typedef i_shader_uniform abstract_type;
181 public:
182 virtual shader_uniform_id id() const = 0;
183 virtual const i_string& name() const = 0;
184 virtual bool shared() const = 0;
185 virtual bool singular() const = 0;
186 virtual bool has_storage() const = 0;
187 virtual shader_uniform_storage storage() const = 0;
188 virtual void set_storage(shader_uniform_storage aStorage) = 0;
189 virtual void clear_storage() = 0;
190 virtual bool has_location() const = 0;
191 virtual shader_uniform_location location() const = 0;
192 virtual void set_location(shader_uniform_location aLocation) = 0;
193 virtual void clear_location() = 0;
194 virtual const abstract_t<shader_value_type>& value() const = 0;
195 virtual abstract_t<shader_value_type>& mutable_value() = 0;
196 virtual void set_value(const abstract_t<shader_value_type>& aValue) = 0;
197 virtual bool is_dirty() const = 0;
198 virtual void clean() const = 0;
199 virtual bool different_type_to(const abstract_t<shader_value_type>& aValue) const = 0;
200 public:
201 template <typename T>
202 void set_value(const T& aValue)
203 {
204 if constexpr (std::is_integral_v<T>)
205 {
206 if constexpr (std::is_same_v<T, bool>)
207 set_value(to_abstract(shader_value_type{ aValue }));
208 else if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
209 set_value(to_abstract(shader_value_type{ aValue }));
210 else if constexpr (std::is_signed_v<T>)
211 set_value(to_abstract(shader_value_type{ static_cast<int32_t>(aValue) }));
212 else if constexpr (std::is_unsigned_v<T>)
213 set_value(to_abstract(shader_value_type{ static_cast<uint32_t>(aValue) }));
214 else
215 set_value(to_abstract(shader_value_type{ static_cast<int32_t>(aValue) }));
216 }
217 else if constexpr (std::is_enum_v<T>)
218 set_value(to_abstract(shader_value_type{ static_cast<int32_t>(aValue) }));
219 else
220 set_value(to_abstract(shader_value_type{ aValue }));
221 }
222 };
223
224 class shader_uniform : public i_shader_uniform
225 {
226 typedef i_shader_uniform base_type;
227 public:
228 typedef base_type abstract_type;
229 private:
230 typedef std::variant<std::monostate, shader_uniform_storage, shader_uniform_location> placement;
231 public:
232 template <typename T>
233 shader_uniform(shader_uniform_id aId, const string& aName, bool aShared, const T& aValue) :
234 iId { aId },
235 iName{ aName },
236 iShared{ aShared },
237 iValue{ aValue },
238 iDirty{ true }
239 {
240 }
241 shader_uniform(const shader_uniform& aOther) :
242 iId{ aOther.iId },
243 iName{ aOther.iName },
244 iShared{ aOther.iShared },
245 iValue{ aOther.iValue },
246 iDirty{ true }
247 {
248 }
249 shader_uniform(shader_uniform&& aOther) noexcept :
250 iId{ std::move(aOther.iId) },
251 iName{ std::move(aOther.iName) },
252 iShared{ std::move(aOther.iShared) },
253 iValue{ std::move(aOther.iValue) },
254 iDirty{ true }
255 {
256 }
257 shader_uniform(const i_shader_uniform& aOther) :
258 iId{ aOther.id() },
259 iName{ aOther.name() },
260 iShared{ aOther.shared()},
261 iValue{ aOther.value() },
262 iDirty{ true }
263 {
264 }
265 public:
266 shader_uniform& operator=(const shader_uniform& aOther)
267 {
268 if (&aOther == this)
269 return *this;
270 this->~shader_uniform();
271 new (this) shader_uniform{ aOther };
272 return *this;
273 }
274 shader_uniform& operator=(shader_uniform&& aOther) noexcept
275 {
276 if (&aOther == this)
277 return *this;
278 iId = std::move(aOther.iId);
279 iName = std::move(aOther.iName);
280 iShared = std::move(aOther.iShared);
281 iValue = std::move(aOther.iValue);
282 iDirty = true;
283 return *this;
284 }
285 public:
286 shader_uniform_id id() const final
287 {
288 return iId;
289 }
290 const i_string& name() const final
291 {
292 return iName;
293 }
294 bool shared() const final
295 {
296 return iShared;
297 }
298 bool singular() const final
299 {
300 if (!value().empty())
301 {
302 switch (value().which())
303 {
304 case shader_data_type::Sampler2D:
305 case shader_data_type::Sampler2DMS:
306 case shader_data_type::Sampler2DRect:
307 return true;
308 }
309 }
310 return false;
311 }
312 bool has_storage() const final
313 {
314 return std::holds_alternative<shader_uniform_storage>(iPlacement);
315 }
316 shader_uniform_storage storage() const final
317 {
318 if (has_storage())
319 return std::get<shader_uniform_storage>(iPlacement);
320 throw unknown_uniform_storage();
321 }
322 void set_storage(shader_uniform_storage aStorage) final
323 {
324 iPlacement = aStorage;
325 }
326 void clear_storage() final
327 {
328 if (has_storage())
329 iPlacement = std::monostate{};
330 }
331 bool has_location() const final
332 {
333 return std::holds_alternative<shader_uniform_location>(iPlacement);
334 }
335 shader_uniform_location location() const final
336 {
337 if (has_location())
338 return std::get<shader_uniform_location>(iPlacement);
339 throw unknown_uniform_location();
340 }
341 void set_location(shader_uniform_location aLocation) final
342 {
343 iPlacement = aLocation;
344 }
345 void clear_location() final
346 {
347 if (has_location())
348 iPlacement = std::monostate{};
349 }
350 const abstract_t<shader_value_type>& value() const final
351 {
352 return iValue;
353 }
354 abstract_t<shader_value_type>& mutable_value() final
355 {
356 iDirty = true;
357 return iValue;
358 }
359 void set_value(const abstract_t<shader_value_type>& aValue) final
360 {
361 if (iValue != aValue)
362 {
363 iValue = aValue;
364 iDirty = true;
365 }
366 }
367 bool is_dirty() const final
368 {
369 return iDirty;
370 }
371 void clean() const final
372 {
373 iDirty = false;
374 }
375 bool different_type_to(const abstract_t<shader_value_type>& aValue) const
376 {
377 if (value().which() != aValue.which())
378 return true;
379 switch (value().which())
380 {
381 case shader_data_type::FloatArray:
382 return value().get<abstract_t<shader_float_array>>().size() !=
383 aValue.get<abstract_t<shader_float_array>>().size();
384 case shader_data_type::DoubleArray:
385 return value().get<abstract_t<shader_double_array>>().size() !=
386 aValue.get<abstract_t<shader_double_array>>().size();
387 default:
388 return false;
389 }
390 }
391 public:
392 shader_uniform_id iId;
393 string iName;
394 bool iShared;
395 placement iPlacement;
396 shader_value_type iValue;
397 mutable bool iDirty;
398 };
399
400 class i_shader_variable
401 {
402 public:
403 typedef i_shader_variable abstract_type;
404 public:
405 virtual const i_string& name() const = 0;
406 virtual shader_variable_location location() const = 0;
407 virtual const i_enum_t<shader_variable_qualifier>& qualifier() const = 0;
408 virtual const i_enum_t<shader_data_type>& type() const = 0;
409 virtual bool has_link() const = 0;
410 virtual const i_shader_variable& link() const = 0;
411 virtual void link(const i_shader_variable& aOther) = 0;
412 virtual void reset_link() = 0;
413 };
414
415 class shader_variable : public i_shader_variable
416 {
417 typedef i_shader_variable base_type;
418 public:
419 typedef base_type abstract_type;
420 public:
421 shader_variable(
422 const string& aName,
423 shader_variable_location aLocation,
424 shader_variable_qualifier aQualifier,
425 shader_data_type aType) :
426 iName{ aName },
427 iLocation{ aLocation },
428 iQualifier{ aQualifier },
429 iType{ aType },
430 iLink{ nullptr }
431 {
432 }
433 shader_variable(const i_shader_variable& aOther) :
434 iName{ aOther.name() },
435 iLocation{ aOther.location() },
436 iQualifier{ aOther.qualifier() },
437 iType{ aOther.type() },
438 iLink{ nullptr }
439 {
440 if (aOther.has_link())
441 link(aOther.link());
442 }
443 public:
444 const i_string& name() const final
445 {
446 return iName;
447 }
448 shader_variable_location location() const final
449 {
450 return iLocation;
451 }
452 const i_enum_t<shader_variable_qualifier>& qualifier() const final
453 {
454 return iQualifier;
455 }
456 const i_enum_t<shader_data_type>& type() const final
457 {
458 return iType;
459 }
460 bool has_link() const final
461 {
462 return iLink != nullptr;
463 }
464 const i_shader_variable& link() const final
465 {
466 if (has_link())
467 return *iLink;
468 throw shader_variable_not_linked();
469 }
470 void link(const i_shader_variable& aOther) final
471 {
472 iLink = &aOther;
473 }
474 void reset_link() final
475 {
476 iLink = nullptr;
477 }
478 public:
479 bool operator<(const shader_variable& aRhs) const
480 {
481 return location() < aRhs.location();
482 }
483 public:
484 string iName;
485 shader_variable_location iLocation;
486 enum_t<shader_variable_qualifier> iQualifier;
487 enum_t<shader_data_type> iType;
488 const i_shader_variable* iLink;
489 };
490
491 class i_rendering_context;
492 class i_shader_program;
493
494 class i_shader : public i_reference_counted
495 {
496 public:
497 typedef i_shader abstract_type;
498 public:
499 typedef abstract_t<shader_value_type> value_type;
500 typedef neolib::i_vector<i_shader_uniform> uniform_list;
501 typedef neolib::i_set<i_shader_variable> variable_list;
502 public:
503 virtual ~i_shader() = default;
504 public:
505 virtual shader_type type() const = 0;
506 virtual const i_string& name() const = 0;
507 virtual bool supports(vertex_buffer_type aBufferType) const = 0;
508 virtual void* handle(const i_shader_program& aProgram) const = 0;
509 virtual bool enabled() const = 0;
510 virtual bool disabled() const = 0;
511 virtual void enable() = 0;
512 virtual void disable() = 0;
513 virtual bool dirty() const = 0;
514 virtual void set_dirty() = 0;
515 virtual void set_clean() = 0;
516 virtual bool uniforms_changed() const = 0;
517 public:
518 virtual const uniform_list& uniforms() const = 0;
519 virtual bool has_shared_uniforms() const = 0;
520 virtual void clear_uniform(shader_uniform_id aUniform) = 0;
521 virtual shader_uniform_id create_uniform(const i_string& aName, bool aShared = false) = 0;
522 virtual shader_uniform_id find_uniform(const i_string& aName) const = 0;
523 virtual void set_uniform(shader_uniform_id aUniform, value_type const& aValue) = 0;
524 virtual void clear_uniform_storage(shader_uniform_id aUniform) = 0;
525 virtual void update_uniform_storage(shader_uniform_id aUniform, shader_uniform_storage aStorage) = 0;
526 virtual void clear_uniform_location(shader_uniform_id aUniform) = 0;
527 virtual void update_uniform_location(shader_uniform_id aUniform, shader_uniform_location aLocation) = 0;
528 virtual const variable_list& in_variables() const = 0;
529 virtual const variable_list& out_variables() const = 0;
530 virtual void clear_variable(const i_string& aName) = 0;
531 virtual i_shader_variable& add_variable(const i_shader_variable& aVariable) = 0;
532 public:
533 virtual void prepare_uniforms(const i_rendering_context& aContext, i_shader_program& aProgram) = 0;
534 virtual void generate_code(const i_shader_program& aProgram, shader_language aLanguage, i_string& aOutput) const = 0;
535 virtual void generate_invoke(const i_shader_program& aProgram, shader_language aLanguage, i_string& aInvokes) const = 0;
536 // helpers
537 public:
538 void set_uniform(const i_string& aName, value_type const& aValue)
539 {
540 auto existing = find_uniform(aName);
541 if (existing == no_uniform)
542 existing = create_uniform(aName);
543 set_uniform(existing, aValue);
544 }
545 template <typename T>
546 void set_uniform(const i_string& aName, const T& aValue)
547 {
548 if constexpr (!std::is_enum_v<T>)
549 set_uniform(aName, to_abstract(shader_value_type{ aValue }));
550 else
551 set_uniform(aName, to_abstract(shader_value_type{ static_cast<int>(aValue) }));
552 }
553 template <std::size_t ArraySize>
554 void set_uniform(const i_string& aName, const float(&aArray)[ArraySize])
555 {
556 set_uniform(aName, shader_float_array{ &aArray[0], &aArray[0] + ArraySize });
557 }
558 template <std::size_t ArraySize>
559 void set_uniform(const i_string& aName, const double(&aArray)[ArraySize])
560 {
561 set_uniform(aName, shader_double_array{ &aArray[0], &aArray[0] + ArraySize });
562 }
563 void set_uniform(const i_string& aName, const float* aArray, std::size_t aArraySize)
564 {
565 set_uniform(aName, shader_float_array{ aArray, aArray + aArraySize });
566 }
567 void set_uniform(const i_string& aName, const double* aArray, std::size_t aArraySize)
568 {
569 set_uniform(aName, shader_double_array{ aArray, aArray + aArraySize });
570 }
571 template <typename T>
572 i_shader_variable& add_in_variable(const i_string& aName, shader_variable_location aLocation, bool aFlat = false)
573 {
574 return add_variable(
575 shader_variable{
576 aName,
577 aLocation,
578 aFlat ? shader_variable_qualifier::In | shader_variable_qualifier::Flat : shader_variable_qualifier::In,
580 }
581 template <typename T>
582 i_shader_variable& add_out_variable(const i_string& aName, shader_variable_location aLocation, bool aFlat = false)
583 {
584 return add_variable(
585 shader_variable{
586 aName,
587 aLocation,
588 aFlat ? shader_variable_qualifier::Out | shader_variable_qualifier::Flat : shader_variable_qualifier::Out,
590 }
591 };
592
593 template <typename Shader, typename... Args>
594 inline neolib::ref_ptr<i_shader> make_shader(Args&&... aArgs)
595 {
596 return neolib::ref_ptr<i_shader>(new Shader{ std::forward<Args>(aArgs)... });
597 }
598}
#define end_declare_enum(enumName)
Definition i_enum.hpp:62
#define declare_enum_string_explicit(enumName, enumEnumerator, enumString)
Definition i_enum.hpp:60
#define declare_enum_string(enumName, enumEnumerator)
Definition i_enum.hpp:59
#define begin_declare_enum(enumName)
Definition i_enum.hpp:52
uint32_t id
shader_data_type
Definition i_shader.hpp:69
shader_variable_qualifier
Definition i_shader.hpp:47
constexpr style_aspect operator&(style_aspect aLhs, style_aspect aRhs)
Definition i_style.hpp:60
bool operator<(const basic_rect< CoordinateType, CoordinateSystem > &left, const basic_rect< CoordinateType, CoordinateSystem > &right)
bool operator==(const basic_rect< CoordinateType, CoordinateSystem > &left, const basic_rect< CoordinateType, CoordinateSystem > &right)
constexpr style_aspect operator|(style_aspect aLhs, style_aspect aRhs)
Definition i_style.hpp:55
shader_language
Definition i_shader.hpp:64
basic_size< coordinate > size
const abstract_t< T > & to_abstract(const T &aArgument)
Definition neolib.hpp:181
uint32_t cookie
Definition i_jar.hpp:44
ref_ptr< ConcreteType > make_ref(Args &&... args)
Definition plf_hive.h:79