... | ... |
@@ -6,6 +6,7 @@ |
6 | 6 |
#include <cstring> |
7 | 7 |
#include <fstream> |
8 | 8 |
#include <ios> |
9 |
+#include <iterator> |
|
9 | 10 |
#include <list> |
10 | 11 |
#include <memory> |
11 | 12 |
#include <regex> |
... | ... |
@@ -866,3 +867,104 @@ Shader::UniformBuffer * Shader::uniform_buffer_( |
866 | 867 |
// Return |
867 | 868 |
return &uniform_buffer; |
868 | 869 |
} |
870 |
+ |
|
871 |
+ |
|
872 |
+Shader & Shader::texture( |
|
873 |
+ std::string const & name, |
|
874 |
+ GLuint texture, |
|
875 |
+ GLenum target, |
|
876 |
+ bool required |
|
877 |
+) |
|
878 |
+{ |
|
879 |
+ // Get limits. |
|
880 |
+ static auto const max_combined_texture_image_units = get_integer_<GLuint>( |
|
881 |
+ GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS |
|
882 |
+ ); |
|
883 |
+ |
|
884 |
+ // Define data. |
|
885 |
+ struct Unit |
|
886 |
+ { |
|
887 |
+ GLuint unit; |
|
888 |
+ GLenum target; |
|
889 |
+ GLuint texture; |
|
890 |
+ }; |
|
891 |
+ using Units = std::list<Unit>; |
|
892 |
+ using Textures = std::unordered_map<GLuint, Units::iterator>; |
|
893 |
+ static auto units = Units{}; |
|
894 |
+ static auto textures = Textures{}; |
|
895 |
+ |
|
896 |
+ // Define helpers. |
|
897 |
+ static auto const unit_to_front = [&](Units::iterator const & unit_it) |
|
898 |
+ { |
|
899 |
+ if (unit_it != units.begin()) |
|
900 |
+ units.splice(units.begin(), units, unit_it, std::next(unit_it)); |
|
901 |
+ }; |
|
902 |
+ static auto const target_name = [](GLenum target) |
|
903 |
+ { |
|
904 |
+ return |
|
905 |
+ STR_COND(target, GL_TEXTURE_1D) |
|
906 |
+ STR_COND(target, GL_TEXTURE_2D) |
|
907 |
+ STR_COND(target, GL_TEXTURE_3D) |
|
908 |
+ STR_COND(target, GL_TEXTURE_1D_ARRAY) |
|
909 |
+ STR_COND(target, GL_TEXTURE_2D_ARRAY) |
|
910 |
+ STR_COND(target, GL_TEXTURE_RECTANGLE) |
|
911 |
+ STR_COND(target, GL_TEXTURE_CUBE_MAP) |
|
912 |
+ STR_COND(target, GL_TEXTURE_CUBE_MAP_ARRAY) |
|
913 |
+ STR_COND(target, GL_TEXTURE_BUFFER) |
|
914 |
+ STR_COND(target, GL_TEXTURE_2D_MULTISAMPLE) |
|
915 |
+ STR_COND(target, GL_TEXTURE_2D_MULTISAMPLE_ARRAY) |
|
916 |
+ STR(std::hex << std::showbase << target); |
|
917 |
+ }; |
|
918 |
+ |
|
919 |
+ // Check textures. |
|
920 |
+ auto texture_it = textures.find(texture); |
|
921 |
+ if (texture_it != textures.end() && texture_it->second->texture == texture) |
|
922 |
+ { |
|
923 |
+ // Check for errors. |
|
924 |
+ if (texture_it->second->target != target) |
|
925 |
+ throw std::runtime_error{STR( |
|
926 |
+ "Failed to set texture " << texture << "; " << |
|
927 |
+ "expected target " << target_name(texture_it->second->target) |
|
928 |
+ << " but got " << target_name(target) << "." |
|
929 |
+ )}; |
|
930 |
+ |
|
931 |
+ // Update units. |
|
932 |
+ unit_to_front(texture_it->second); |
|
933 |
+ } |
|
934 |
+ else |
|
935 |
+ { |
|
936 |
+ // Update units. |
|
937 |
+ if (units.size() < max_combined_texture_image_units) |
|
938 |
+ { |
|
939 |
+ units.emplace_front(Unit{(GLuint)units.size(), target, texture}); |
|
940 |
+ } |
|
941 |
+ else |
|
942 |
+ { |
|
943 |
+ unit_to_front(std::prev(units.end())); |
|
944 |
+ units.front().target = target; |
|
945 |
+ units.front().texture = texture; |
|
946 |
+ } |
|
947 |
+ |
|
948 |
+ // Update textures. |
|
949 |
+ if (texture_it == textures.end()) |
|
950 |
+ textures.emplace(texture, units.begin()); |
|
951 |
+ else |
|
952 |
+ texture_it->second = units.begin(); |
|
953 |
+ |
|
954 |
+ // Bind. |
|
955 |
+ auto const error = STR( |
|
956 |
+ "Failed to bind texture " << texture << " to target " << |
|
957 |
+ target_name(target) |
|
958 |
+ ); |
|
959 |
+ error_(error, "unprocessed previous error"); |
|
960 |
+ glActiveTexture(GL_TEXTURE0 + units.front().unit); |
|
961 |
+ glBindTexture(target, texture); |
|
962 |
+ error_(error, "wrong target?"); |
|
963 |
+ } |
|
964 |
+ |
|
965 |
+ // Set uniform. |
|
966 |
+ uniform(name, (GLint)units.front().unit, required); |
|
967 |
+ |
|
968 |
+ // Return. |
|
969 |
+ return *this; |
|
970 |
+} |
... | ... |
@@ -33,6 +33,7 @@ GLSHADER_INIT_(Shader::root_, {}) |
33 | 33 |
GLSHADER_INIT_(Shader::defines_, {}) |
34 | 34 |
GLSHADER_INIT_(Shader::verts_, {}) |
35 | 35 |
GLSHADER_INIT_(Shader::frags_, {}) |
36 |
+GLSHADER_INIT_(Shader::uniform_buffers_, {}) |
|
36 | 37 |
|
37 | 38 |
|
38 | 39 |
template<typename Type> |
... | ... |
@@ -402,7 +403,8 @@ Shader::Shader(Paths const & paths) |
402 | 403 |
program_name_{STR( |
403 | 404 |
"shader program " << STR_JOIN(", ", it, "'" << it << "'", paths) |
404 | 405 |
)}, |
405 |
- uniforms_{} |
|
406 |
+ uniforms_{}, |
|
407 |
+ uniform_blocks_{} |
|
406 | 408 |
{ |
407 | 409 |
// Get label limits. |
408 | 410 |
static auto const max_label_length = get_integer_<GLsizei>( |
... | ... |
@@ -560,6 +562,39 @@ Shader::Shader(Paths const & paths) |
560 | 562 |
uniforms_.emplace(name, Uniform{location, false}); |
561 | 563 |
} |
562 | 564 |
); |
565 |
+ |
|
566 |
+ // Initialize uniform blocks. |
|
567 |
+ for_variable_( |
|
568 |
+ program_, |
|
569 |
+ GL_ACTIVE_UNIFORM_BLOCKS, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, |
|
570 |
+ [&](GLuint index, GLsizei max_length, GLchar * name) |
|
571 |
+ { |
|
572 |
+ if (!(GLEW_VERSION_3_1 || GLEW_ARB_uniform_buffer_object)) |
|
573 |
+ throw std::runtime_error{STR( |
|
574 |
+ "Failed to initialize uniform block; " << |
|
575 |
+ "ARB_uniform_buffer_object not available." |
|
576 |
+ )}; |
|
577 |
+ glGetActiveUniformBlockName( |
|
578 |
+ program_, index, max_length, |
|
579 |
+ nullptr, name |
|
580 |
+ ); |
|
581 |
+ auto error = STR( |
|
582 |
+ "Failed to initialize uniform block '" << name << "' of " |
|
583 |
+ << program_name_ |
|
584 |
+ ); |
|
585 |
+ auto size = GLsizei{}; |
|
586 |
+ glGetActiveUniformBlockiv( |
|
587 |
+ program_, index, GL_UNIFORM_BLOCK_DATA_SIZE, (GLint *)&size |
|
588 |
+ ); |
|
589 |
+ auto & uniform_buffer = *uniform_buffer_( |
|
590 |
+ error, name, false, size |
|
591 |
+ ); |
|
592 |
+ glUniformBlockBinding(program_, index, uniform_buffer.binding); |
|
593 |
+ uniform_blocks_.emplace( |
|
594 |
+ name, UniformBlock{uniform_buffer} |
|
595 |
+ ); |
|
596 |
+ } |
|
597 |
+ ); |
|
563 | 598 |
} |
564 | 599 |
catch (...) |
565 | 600 |
{ |
... | ... |
@@ -573,9 +608,10 @@ Shader::Shader(Paths const & paths) |
573 | 608 |
|
574 | 609 |
Shader::Shader(Shader && other) noexcept |
575 | 610 |
: |
576 |
- program_ {other.program_}, |
|
577 |
- program_name_{std::move(other.program_name_)}, |
|
578 |
- uniforms_ {std::move(other.uniforms_)} |
|
611 |
+ program_ {other.program_}, |
|
612 |
+ program_name_ {std::move(other.program_name_)}, |
|
613 |
+ uniforms_ {std::move(other.uniforms_)}, |
|
614 |
+ uniform_blocks_{std::move(other.uniform_blocks_)} |
|
579 | 615 |
{ |
580 | 616 |
other.program_ = 0; |
581 | 617 |
} |
... | ... |
@@ -635,6 +671,14 @@ void Shader::validate_() const |
635 | 671 |
return uniform.set; |
636 | 672 |
} |
637 | 673 |
); |
674 |
+ |
|
675 |
+ // Validate uniform blocks. |
|
676 |
+ uniforms_validate_(validate_error, "uniform block", uniform_blocks_, |
|
677 |
+ [](UniformBlock const & uniform_block) |
|
678 |
+ { |
|
679 |
+ return uniform_block.buffer.set; |
|
680 |
+ } |
|
681 |
+ ); |
|
638 | 682 |
} |
639 | 683 |
|
640 | 684 |
|
... | ... |
@@ -665,11 +709,160 @@ Shader::Uniform * Shader::uniform_( |
665 | 709 |
|
666 | 710 |
// Error if required. |
667 | 711 |
if (required) |
712 |
+ { |
|
713 |
+ auto error_hint = std::string{}; |
|
714 |
+ if (uniform_blocks_.find(name) != uniform_blocks_.end()) |
|
715 |
+ error_hint = " (did you mean the uniform block?)"; |
|
716 |
+ throw std::runtime_error{STR( |
|
717 |
+ error << "; " << |
|
718 |
+ "uniform required but not found" << error_hint << "." |
|
719 |
+ )}; |
|
720 |
+ } |
|
721 |
+ |
|
722 |
+ // Return. |
|
723 |
+ return nullptr; |
|
724 |
+} |
|
725 |
+ |
|
726 |
+ |
|
727 |
+Shader::UniformBlock * Shader::uniform_block_( |
|
728 |
+ std::string const & error, |
|
729 |
+ std::string const & name, |
|
730 |
+ bool required, |
|
731 |
+ GLsizeiptr size |
|
732 |
+) |
|
733 |
+{ |
|
734 |
+ // Return if found. |
|
735 |
+ auto it = uniform_blocks_.find(name); |
|
736 |
+ if (it != uniform_blocks_.end()) |
|
737 |
+ { |
|
738 |
+ auto & uniform_block = it->second; |
|
739 |
+ if (size != uniform_block.buffer.size) |
|
740 |
+ throw std::runtime_error{STR( |
|
741 |
+ error << "; " << |
|
742 |
+ "expected size " << uniform_block.buffer.size << " but got " << |
|
743 |
+ size << "." |
|
744 |
+ )}; |
|
745 |
+ return &uniform_block; |
|
746 |
+ } |
|
747 |
+ |
|
748 |
+ // Error if required. |
|
749 |
+ if (required) |
|
750 |
+ { |
|
751 |
+ auto error_hint = std::string{}; |
|
752 |
+ if (uniforms_.find(name) != uniforms_.end()) |
|
753 |
+ error_hint = " (did you mean the uniform?)"; |
|
668 | 754 |
throw std::runtime_error{STR( |
669 | 755 |
error << "; " << |
670 |
- "uniform required but not found." |
|
756 |
+ "uniform block required but not found" << error_hint << "." |
|
671 | 757 |
)}; |
758 |
+ } |
|
672 | 759 |
|
673 | 760 |
// Return. |
674 | 761 |
return nullptr; |
675 | 762 |
} |
763 |
+ |
|
764 |
+ |
|
765 |
+Shader::UniformBuffer * Shader::uniform_buffer_( |
|
766 |
+ std::string const & error, |
|
767 |
+ std::string const & name, |
|
768 |
+ bool required, |
|
769 |
+ GLsizeiptr size |
|
770 |
+) |
|
771 |
+{ |
|
772 |
+ // Get uniform block / buffer limits. |
|
773 |
+ static auto const max_uniform_block_size = get_integer_<GLuint>( |
|
774 |
+ GL_MAX_UNIFORM_BLOCK_SIZE |
|
775 |
+ ); |
|
776 |
+ static auto const max_uniform_buffer_bindings = get_integer_<GLuint>( |
|
777 |
+ GL_MAX_UNIFORM_BUFFER_BINDINGS |
|
778 |
+ ); |
|
779 |
+ |
|
780 |
+ // Define next binding. |
|
781 |
+ static auto next_uniform_buffer_binding_ = GLuint{0}; |
|
782 |
+ |
|
783 |
+ // Return if found. |
|
784 |
+ auto it = uniform_buffers_.find(name); |
|
785 |
+ if (it != uniform_buffers_.end()) |
|
786 |
+ { |
|
787 |
+ auto & uniform_buffer = it->second; |
|
788 |
+ if (size != uniform_buffer.size) |
|
789 |
+ throw std::runtime_error{STR( |
|
790 |
+ error << "; " << |
|
791 |
+ "expected size " << uniform_buffer.size << " but got " << size |
|
792 |
+ << "." |
|
793 |
+ )}; |
|
794 |
+ return &uniform_buffer; |
|
795 |
+ } |
|
796 |
+ |
|
797 |
+ // Error if required. |
|
798 |
+ if (required) |
|
799 |
+ throw std::runtime_error{STR( |
|
800 |
+ error << "; " << |
|
801 |
+ "uniform buffer required but not found." |
|
802 |
+ )}; |
|
803 |
+ |
|
804 |
+ // Set create error. |
|
805 |
+ auto create_error = STR( |
|
806 |
+ error << ":\n" << |
|
807 |
+ "Failed to create uniform buffer '" << name << "'" |
|
808 |
+ ); |
|
809 |
+ |
|
810 |
+ // Check availability. |
|
811 |
+ if (!(GLEW_VERSION_3_1 || GLEW_ARB_uniform_buffer_object)) |
|
812 |
+ throw std::runtime_error{STR( |
|
813 |
+ create_error << "; " << |
|
814 |
+ "ARB_uniform_buffer_object not available." |
|
815 |
+ )}; |
|
816 |
+ |
|
817 |
+ // Create storage. |
|
818 |
+ auto emplace = uniform_buffers_.emplace(name, UniformBuffer{}); |
|
819 |
+ if (!emplace.second) |
|
820 |
+ throw std::runtime_error{STR( |
|
821 |
+ create_error << "; " << |
|
822 |
+ "already exists." |
|
823 |
+ )}; |
|
824 |
+ auto & uniform_buffer = emplace.first->second; |
|
825 |
+ |
|
826 |
+ // Check for errors. |
|
827 |
+ if (size > max_uniform_block_size) |
|
828 |
+ throw std::runtime_error{STR( |
|
829 |
+ create_error << "; " << |
|
830 |
+ "buffer has size " << size << " but max size is " << |
|
831 |
+ max_uniform_block_size << "." |
|
832 |
+ )}; |
|
833 |
+ if (next_uniform_buffer_binding_ >= max_uniform_buffer_bindings) |
|
834 |
+ throw std::runtime_error{STR( |
|
835 |
+ create_error << "; " << |
|
836 |
+ "buffer would have binding " << next_uniform_buffer_binding_ << |
|
837 |
+ " but max bindings is " << max_uniform_buffer_bindings << "." |
|
838 |
+ )}; |
|
839 |
+ |
|
840 |
+ // Generate and bind. |
|
841 |
+ glGenBuffers(1, &uniform_buffer.buffer); |
|
842 |
+ glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer.buffer), |
|
843 |
+ |
|
844 |
+ // Allocate size. |
|
845 |
+ uniform_buffer.size = size; |
|
846 |
+ error_(create_error, "unprocessed previous error"); |
|
847 |
+ glBufferData( |
|
848 |
+ GL_UNIFORM_BUFFER, |
|
849 |
+ uniform_buffer.size, |
|
850 |
+ nullptr, |
|
851 |
+ GL_DYNAMIC_DRAW |
|
852 |
+ ); |
|
853 |
+ error_(create_error); |
|
854 |
+ |
|
855 |
+ // Allocate binding and bind. |
|
856 |
+ uniform_buffer.binding = next_uniform_buffer_binding_++; |
|
857 |
+ glBindBufferBase( |
|
858 |
+ GL_UNIFORM_BUFFER, |
|
859 |
+ uniform_buffer.binding, |
|
860 |
+ uniform_buffer.buffer |
|
861 |
+ ); |
|
862 |
+ |
|
863 |
+ // Mark as unset. |
|
864 |
+ uniform_buffer.set = false; |
|
865 |
+ |
|
866 |
+ // Return |
|
867 |
+ return &uniform_buffer; |
|
868 |
+} |
... | ... |
@@ -5,6 +5,7 @@ |
5 | 5 |
#include <cerrno> |
6 | 6 |
#include <cstring> |
7 | 7 |
#include <fstream> |
8 |
+#include <ios> |
|
8 | 9 |
#include <list> |
9 | 10 |
#include <memory> |
10 | 11 |
#include <regex> |
... | ... |
@@ -44,6 +45,28 @@ static Type get_integer_(GLenum name, bool supported = true) |
44 | 45 |
} |
45 | 46 |
|
46 | 47 |
|
48 |
+void Shader::error_( |
|
49 |
+ std::string const & error, |
|
50 |
+ std::string const & error_hint |
|
51 |
+) |
|
52 |
+{ |
|
53 |
+ auto gl_error = glGetError(); |
|
54 |
+ if (gl_error != GL_NO_ERROR) |
|
55 |
+ throw std::runtime_error{STR( |
|
56 |
+ error << "; " << |
|
57 |
+ "got error " << ( |
|
58 |
+ STR_COND(gl_error, GL_INVALID_ENUM) |
|
59 |
+ STR_COND(gl_error, GL_INVALID_VALUE) |
|
60 |
+ STR_COND(gl_error, GL_INVALID_OPERATION) |
|
61 |
+ STR_COND(gl_error, GL_OUT_OF_MEMORY) |
|
62 |
+ STR(std::hex << std::showbase << gl_error) |
|
63 |
+ ) << |
|
64 |
+ (error_hint.empty() ? "" : STR(" (" << error_hint << ")")) << |
|
65 |
+ "." |
|
66 |
+ )}; |
|
67 |
+} |
|
68 |
+ |
|
69 |
+ |
|
47 | 70 |
static void info_log_action_( |
48 | 71 |
std::string const & error, |
49 | 72 |
void (action)(GLuint object), |
... | ... |
@@ -378,7 +401,8 @@ Shader::Shader(Paths const & paths) |
378 | 401 |
program_{0}, |
379 | 402 |
program_name_{STR( |
380 | 403 |
"shader program " << STR_JOIN(", ", it, "'" << it << "'", paths) |
381 |
- )} |
|
404 |
+ )}, |
|
405 |
+ uniforms_{} |
|
382 | 406 |
{ |
383 | 407 |
// Get label limits. |
384 | 408 |
static auto const max_label_length = get_integer_<GLsizei>( |
... | ... |
@@ -518,6 +542,24 @@ Shader::Shader(Paths const & paths) |
518 | 542 |
)}; |
519 | 543 |
} |
520 | 544 |
); |
545 |
+ |
|
546 |
+ // Initialize uniforms. |
|
547 |
+ for_variable_( |
|
548 |
+ program_, |
|
549 |
+ GL_ACTIVE_UNIFORMS, GL_ACTIVE_UNIFORM_MAX_LENGTH, |
|
550 |
+ [&](GLuint index, GLsizei max_length, GLchar * name) |
|
551 |
+ { |
|
552 |
+ GLint size{}; |
|
553 |
+ GLenum type{}; |
|
554 |
+ glGetActiveUniform( |
|
555 |
+ program_, index, max_length, |
|
556 |
+ nullptr, &size, &type, name |
|
557 |
+ ); |
|
558 |
+ auto location = glGetUniformLocation(program_, name); |
|
559 |
+ if (location != -1) |
|
560 |
+ uniforms_.emplace(name, Uniform{location, false}); |
|
561 |
+ } |
|
562 |
+ ); |
|
521 | 563 |
} |
522 | 564 |
catch (...) |
523 | 565 |
{ |
... | ... |
@@ -532,7 +574,8 @@ Shader::Shader(Paths const & paths) |
532 | 574 |
Shader::Shader(Shader && other) noexcept |
533 | 575 |
: |
534 | 576 |
program_ {other.program_}, |
535 |
- program_name_{std::move(other.program_name_)} |
|
577 |
+ program_name_{std::move(other.program_name_)}, |
|
578 |
+ uniforms_ {std::move(other.uniforms_)} |
|
536 | 579 |
{ |
537 | 580 |
other.program_ = 0; |
538 | 581 |
} |
... | ... |
@@ -545,6 +588,31 @@ Shader::~Shader() |
545 | 588 |
} |
546 | 589 |
|
547 | 590 |
|
591 |
+template<typename Uniforms, typename Set> |
|
592 |
+static void uniforms_validate_( |
|
593 |
+ std::string const & error, |
|
594 |
+ std::string const & uniform_type, |
|
595 |
+ Uniforms uniforms, |
|
596 |
+ Set set |
|
597 |
+) |
|
598 |
+{ |
|
599 |
+ // Find. |
|
600 |
+ auto it = std::find_if(uniforms.begin(), uniforms.end(), |
|
601 |
+ [&](typename Uniforms::value_type const & it) |
|
602 |
+ { |
|
603 |
+ return !set(it.second); |
|
604 |
+ } |
|
605 |
+ ); |
|
606 |
+ |
|
607 |
+ // Error if not found. |
|
608 |
+ if (it != uniforms.end()) |
|
609 |
+ throw std::runtime_error{STR( |
|
610 |
+ error << "; " << |
|
611 |
+ uniform_type << " '" << it->first << "' not set." |
|
612 |
+ )}; |
|
613 |
+} |
|
614 |
+ |
|
615 |
+ |
|
548 | 616 |
void Shader::validate_() const |
549 | 617 |
{ |
550 | 618 |
// Set error. |
... | ... |
@@ -559,6 +627,14 @@ void Shader::validate_() const |
559 | 627 |
|
560 | 628 |
// Assert current. |
561 | 629 |
current_(validate_error); |
630 |
+ |
|
631 |
+ // Validate uniforms. |
|
632 |
+ uniforms_validate_(validate_error, "uniform", uniforms_, |
|
633 |
+ [](Uniform const & uniform) |
|
634 |
+ { |
|
635 |
+ return uniform.set; |
|
636 |
+ } |
|
637 |
+ ); |
|
562 | 638 |
} |
563 | 639 |
|
564 | 640 |
|
... | ... |
@@ -571,3 +647,29 @@ void Shader::current_(std::string const & error) const |
571 | 647 |
"shader program not current." |
572 | 648 |
)}; |
573 | 649 |
} |
650 |
+ |
|
651 |
+ |
|
652 |
+Shader::Uniform * Shader::uniform_( |
|
653 |
+ std::string const & error, |
|
654 |
+ std::string const & name, |
|
655 |
+ bool required |
|
656 |
+) |
|
657 |
+{ |
|
658 |
+ // Return if found. |
|
659 |
+ auto it = uniforms_.find(name); |
|
660 |
+ if (it != uniforms_.end()) |
|
661 |
+ { |
|
662 |
+ auto & uniform = it->second; |
|
663 |
+ return &uniform; |
|
664 |
+ } |
|
665 |
+ |
|
666 |
+ // Error if required. |
|
667 |
+ if (required) |
|
668 |
+ throw std::runtime_error{STR( |
|
669 |
+ error << "; " << |
|
670 |
+ "uniform required but not found." |
|
671 |
+ )}; |
|
672 |
+ |
|
673 |
+ // Return. |
|
674 |
+ return nullptr; |
|
675 |
+} |
... | ... |
@@ -23,10 +23,15 @@ |
23 | 23 |
using Here = std::tuple<std::string, int, std::string>; |
24 | 24 |
|
25 | 25 |
|
26 |
+constexpr auto max_length_workaround = 4096; |
|
27 |
+ |
|
28 |
+ |
|
26 | 29 |
// NOLINTNEXTLINE |
27 | 30 |
#define GLSHADER_INIT_(NAME, INIT) decltype(NAME) NAME INIT; |
28 | 31 |
GLSHADER_INIT_(Shader::root_, {}) |
29 | 32 |
GLSHADER_INIT_(Shader::defines_, {}) |
33 |
+GLSHADER_INIT_(Shader::verts_, {}) |
|
34 |
+GLSHADER_INIT_(Shader::frags_, {}) |
|
30 | 35 |
|
31 | 36 |
|
32 | 37 |
template<typename Type> |
... | ... |
@@ -340,6 +345,34 @@ static std::string source_( |
340 | 345 |
} |
341 | 346 |
|
342 | 347 |
|
348 |
+template<typename Function> |
|
349 |
+static void for_variable_( |
|
350 |
+ GLuint program, |
|
351 |
+ GLenum count_enum, |
|
352 |
+ GLenum max_length_enum, |
|
353 |
+ Function function |
|
354 |
+) |
|
355 |
+{ |
|
356 |
+ // Get count. |
|
357 |
+ auto count = GLuint{}; |
|
358 |
+ glGetProgramiv(program, count_enum, (GLint *)&count); |
|
359 |
+ |
|
360 |
+ // Get max length. |
|
361 |
+ auto max_length = GLsizei{}; |
|
362 |
+ glGetProgramiv(program, max_length_enum, &max_length); |
|
363 |
+ |
|
364 |
+ // Work around driver bugs. |
|
365 |
+ if (max_length == 0 && count != 0) |
|
366 |
+ max_length = max_length_workaround; |
|
367 |
+ |
|
368 |
+ // Allocate and call function. |
|
369 |
+ // NOLINTNEXTLINE |
|
370 |
+ auto name = std::unique_ptr<GLchar[]>(new GLchar[max_length]); |
|
371 |
+ for (auto index = GLuint{0}; index < count; ++index) |
|
372 |
+ function(index, max_length, &name[0]); |
|
373 |
+} |
|
374 |
+ |
|
375 |
+ |
|
343 | 376 |
Shader::Shader(Paths const & paths) |
344 | 377 |
: |
345 | 378 |
program_{0}, |
... | ... |
@@ -441,6 +474,19 @@ Shader::Shader(Paths const & paths) |
441 | 474 |
); |
442 | 475 |
} |
443 | 476 |
|
477 |
+ // Set vertex input locations. |
|
478 |
+ for (auto const & vert : verts_) |
|
479 |
+ glBindAttribLocation( |
|
480 |
+ program_, vert.second, vert.first.c_str() |
|
481 |
+ ); |
|
482 |
+ |
|
483 |
+ // Set fragment output locations. |
|
484 |
+ if (GLEW_VERSION_3_0) |
|
485 |
+ for (auto const & frag : frags_) |
|
486 |
+ glBindFragDataLocation( |
|
487 |
+ program_, frag.second, frag.first.c_str() |
|
488 |
+ ); |
|
489 |
+ |
|
444 | 490 |
// Link program. |
445 | 491 |
info_log_action_( |
446 | 492 |
STR("Failed to link " << program_name_), |
... | ... |
@@ -451,6 +497,27 @@ Shader::Shader(Paths const & paths) |
451 | 497 |
// Detach shaders. |
452 | 498 |
for (auto const & shader : shaders) |
453 | 499 |
glDetachShader(program_, shader); |
500 |
+ |
|
501 |
+ // Initialize vertex inputs. |
|
502 |
+ for_variable_( |
|
503 |
+ program_, |
|
504 |
+ GL_ACTIVE_ATTRIBUTES, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, |
|
505 |
+ [&](GLuint index, GLsizei max_length, GLchar * name) |
|
506 |
+ { |
|
507 |
+ GLint size{}; |
|
508 |
+ GLenum type{}; |
|
509 |
+ glGetActiveAttrib( |
|
510 |
+ program_, index, max_length, |
|
511 |
+ nullptr, &size, &type, name |
|
512 |
+ ); |
|
513 |
+ auto location = glGetAttribLocation(program_, name); |
|
514 |
+ if (location != -1 && verts_.find(name) == verts_.end()) |
|
515 |
+ throw std::runtime_error{STR( |
|
516 |
+ "Failed to initialize vertex input '" << name << |
|
517 |
+ "' of " << program_name_ << "." |
|
518 |
+ )}; |
|
519 |
+ } |
|
520 |
+ ); |
|
454 | 521 |
} |
455 | 522 |
catch (...) |
456 | 523 |
{ |
... | ... |
@@ -5,6 +5,7 @@ |
5 | 5 |
#include <cerrno> |
6 | 6 |
#include <cstring> |
7 | 7 |
#include <fstream> |
8 |
+#include <list> |
|
8 | 9 |
#include <memory> |
9 | 10 |
#include <regex> |
10 | 11 |
#include <sstream> |
... | ... |
@@ -81,21 +82,48 @@ static std::string source_( |
81 | 82 |
std::string const & error, |
82 | 83 |
std::string const & path, |
83 | 84 |
std::string const & root, |
84 |
- Shader::Defines const & defines |
|
85 |
+ Shader::Defines const & defines, |
|
86 |
+ std::string extension_behavior = {}, |
|
87 |
+ std::list<Here> included_by = {} |
|
85 | 88 |
) |
86 | 89 |
{ |
87 | 90 |
// Set here error. |
88 |
- auto const here_error = [](Here const & here) |
|
91 |
+ auto const here_error = [&](std::list<Here> const & include_here) |
|
89 | 92 |
{ |
90 |
- return STR( |
|
91 |
- std::get<0>(here) << ":" << |
|
92 |
- std::get<1>(here) << ": " << |
|
93 |
- std::get<2>(here) |
|
93 |
+ return STR_JOIN( |
|
94 |
+ "\n", |
|
95 |
+ it, |
|
96 |
+ std::get<0>(it) << ":" << |
|
97 |
+ std::get<1>(it) << ": " << |
|
98 |
+ std::get<2>(it), |
|
99 |
+ include_here |
|
94 | 100 |
); |
95 | 101 |
}; |
96 | 102 |
|
103 |
+ // Set include helper. |
|
104 |
+ auto const include_here = [&](Here const & here) |
|
105 |
+ { |
|
106 |
+ auto include_here = included_by; |
|
107 |
+ include_here.push_front(here); |
|
108 |
+ return include_here; |
|
109 |
+ }; |
|
110 |
+ |
|
97 | 111 |
// Set full path. |
98 |
- auto path_full = path; |
|
112 |
+ auto path_full = std::string{}; |
|
113 |
+ { |
|
114 |
+ auto istream = std::istringstream(path); |
|
115 |
+ auto part = std::string{}; |
|
116 |
+ auto parts = std::vector<std::string>{}; |
|
117 |
+ parts.reserve((size_t)std::count(path.begin(), path.end(), '/') + 1); |
|
118 |
+ while (std::getline(istream, part, '/')) |
|
119 |
+ { |
|
120 |
+ if (part == ".." && !parts.empty()) |
|
121 |
+ parts.pop_back(); |
|
122 |
+ if (part != ".." && part != ".") |
|
123 |
+ parts.push_back(std::move(part)); |
|
124 |
+ } |
|
125 |
+ path_full = STR_JOIN('/', it, it, parts); |
|
126 |
+ } |
|
99 | 127 |
if (!root.empty()) |
100 | 128 |
path_full = STR(root << "/" << path_full); |
101 | 129 |
|
... | ... |
@@ -105,6 +133,7 @@ static std::string source_( |
105 | 133 |
throw std::runtime_error{STR( |
106 | 134 |
error << "; " << |
107 | 135 |
"could not open file '" << path_full << "':\n" << |
136 |
+ here_error(included_by) << (included_by.empty() ? "" : ":\n") << |
|
108 | 137 |
std::strerror(errno) |
109 | 138 |
)}; |
110 | 139 |
|
... | ... |
@@ -112,16 +141,21 @@ static std::string source_( |
112 | 141 |
auto ostream = std::ostringstream{}; |
113 | 142 |
|
114 | 143 |
// Define parse regexes. |
115 |
- static auto const re_ignored = std::regex{R"(\s*//.*$)"}; |
|
116 |
- static auto const re_words = std::regex{R"((\w+(?:\s+\w+)*))"}; |
|
117 |
- static auto const re_version = std::regex{R"(\s*#\s*version\s*(.*))"}; |
|
144 |
+ static auto const re_ignored = std::regex{R"(\s*//.*$)"}; |
|
145 |
+ static auto const re_words = std::regex{R"((\w+(?:\s+\w+)*))"}; |
|
146 |
+ static auto const re_spec = std::regex{R"((\w+)\s*:\s*(\w+))"}; |
|
147 |
+ static auto const re_quoted = std::regex{R"((["<])([^">]*)([">]))"}; |
|
148 |
+ static auto const re_version = std::regex{R"(\s*#\s*version\s*(.*))"}; |
|
149 |
+ static auto const re_extension = std::regex{R"(\s*#\s*extension\s*(.*))"}; |
|
150 |
+ static auto const re_include = std::regex{R"(\s*#\s*include\s*(.*))"}; |
|
118 | 151 |
|
119 | 152 |
// Parse. |
120 |
- auto version_number = 0; |
|
121 |
- auto line_number = 0; |
|
122 |
- auto line = std::string{}; |
|
123 |
- auto match = std::smatch{}; |
|
124 |
- auto here = [&]() |
|
153 |
+ auto version_number = 0; |
|
154 |
+ auto extension_enabled = false; |
|
155 |
+ auto line_number = 0; |
|
156 |
+ auto line = std::string{}; |
|
157 |
+ auto match = std::smatch{}; |
|
158 |
+ auto here = [&]() |
|
125 | 159 |
{ |
126 | 160 |
return Here{path_full, line_number, line}; |
127 | 161 |
}; |
... | ... |
@@ -130,6 +164,16 @@ static std::string source_( |
130 | 164 |
// Remove ignored. |
131 | 165 |
auto const content = std::regex_replace(line, re_ignored, ""); |
132 | 166 |
|
167 |
+ // Output `#line`. |
|
168 |
+ auto const line_number_offset = version_number < 330 ? -1 : 0; |
|
169 |
+ if (GLEW_ARB_shading_language_include) |
|
170 |
+ if (!extension_behavior.empty() && extension_behavior != "disable") |
|
171 |
+ ostream |
|
172 |
+ << "#line" << " " |
|
173 |
+ << line_number + line_number_offset << " " |
|
174 |
+ << "\"" << path_full << "\"" |
|
175 |
+ << "\n"; |
|
176 |
+ |
|
133 | 177 |
// Process version. |
134 | 178 |
if (std::regex_match(content, match, re_version)) |
135 | 179 |
{ |
... | ... |
@@ -139,7 +183,7 @@ static std::string source_( |
139 | 183 |
throw std::runtime_error{STR( |
140 | 184 |
error << "; " << |
141 | 185 |
"malformed #version:\n" << |
142 |
- here_error(here()) |
|
186 |
+ here_error(include_here(here())) |
|
143 | 187 |
)}; |
144 | 188 |
auto const version = match.str(1); |
145 | 189 |
|
... | ... |
@@ -148,7 +192,13 @@ static std::string source_( |
148 | 192 |
throw std::runtime_error{STR( |
149 | 193 |
error << "; " << |
150 | 194 |
"found repeated #version:\n" << |
151 |
- here_error(here()) |
|
195 |
+ here_error(include_here(here())) |
|
196 |
+ )}; |
|
197 |
+ if (!included_by.empty()) |
|
198 |
+ throw std::runtime_error{STR( |
|
199 |
+ error << "; " << |
|
200 |
+ "found #version in #include:\n" << |
|
201 |
+ here_error(include_here(here())) |
|
152 | 202 |
)}; |
153 | 203 |
|
154 | 204 |
// Process. |
... | ... |
@@ -156,6 +206,16 @@ static std::string source_( |
156 | 206 |
|
157 | 207 |
// Output. |
158 | 208 |
ostream << line << "\n"; |
209 |
+ if (GLEW_ARB_shading_language_include) |
|
210 |
+ { |
|
211 |
+ if (extension_behavior.empty()) |
|
212 |
+ { |
|
213 |
+ extension_behavior = "enable"; |
|
214 |
+ ostream |
|
215 |
+ << "#extension GL_ARB_shading_language_include : " |
|
216 |
+ << extension_behavior << "\n"; |
|
217 |
+ } |
|
218 |
+ } |
|
159 | 219 |
for (auto const & define : defines) |
160 | 220 |
ostream |
161 | 221 |
<< "#define " |
... | ... |
@@ -163,6 +223,103 @@ static std::string source_( |
163 | 223 |
<< define.second << "\n"; |
164 | 224 |
} |
165 | 225 |
|
226 |
+ // Process extension. |
|
227 |
+ else if (std::regex_match(content, match, re_extension)) |
|
228 |
+ { |
|
229 |
+ // Parse. |
|
230 |
+ auto const spec = match.str(1); |
|
231 |
+ if (!std::regex_match(spec, match, re_spec)) |
|
232 |
+ throw std::runtime_error{STR( |
|
233 |
+ error << "; " << |
|
234 |
+ "malformed #extension:\n" << |
|
235 |
+ here_error(include_here(here())) |
|
236 |
+ )}; |
|
237 |
+ auto const extension = match.str(1); |
|
238 |
+ auto const behavior = match.str(2); |
|
239 |
+ |
|
240 |
+ if (extension == "GL_ARB_shading_language_include") |
|
241 |
+ { |
|
242 |
+ // Check for errors. |
|
243 |
+ if (!included_by.empty()) |
|
244 |
+ throw std::runtime_error{STR( |
|
245 |
+ error << "; " << |
|
246 |
+ "found #extension GL_ARB_shading_language_include " << |
|
247 |
+ "in #include:\n" << |
|
248 |
+ here_error(include_here(here())) |
|
249 |
+ )}; |
|
250 |
+ |
|
251 |
+ // Process. |
|
252 |
+ extension_enabled = behavior != "disable"; |
|
253 |
+ extension_behavior = behavior; |
|
254 |
+ line = ""; |
|
255 |
+ } |
|
256 |
+ |
|
257 |
+ // Output. |
|
258 |
+ ostream << line << "\n"; |
|
259 |
+ } |
|
260 |
+ |
|
261 |
+ // Process include. |
|
262 |
+ else if (std::regex_match(content, match, re_include)) |
|
263 |
+ { |
|
264 |
+ // Parse. |
|
265 |
+ auto const quoted = match.str(1); |
|
266 |
+ if (!std::regex_match(quoted, match, re_quoted)) |
|
267 |
+ throw std::runtime_error{STR( |
|
268 |
+ error << "; " << |
|
269 |
+ "malformed #include:\n" << |
|
270 |
+ here_error(include_here(here())) |
|
271 |
+ )}; |
|
272 |
+ auto const quote_open = match.str(1); |
|
273 |
+ auto const include_path = match.str(2); |
|
274 |
+ auto const quote_close = match.str(3); |
|
275 |
+ |
|
276 |
+ // Check for errors. |
|
277 |
+ if (!( |
|
278 |
+ (quote_open == "\"" && quote_close == "\"") || |
|
279 |
+ (quote_open == "<" && quote_close == ">" ) |
|
280 |
+ )) |
|
281 |
+ throw std::runtime_error{STR( |
|
282 |
+ error << "; " << |
|
283 |
+ "mismatched #include quotes '" << quote_open << "' and '" |
|
284 |
+ << quote_close << "':\n" << |
|
285 |
+ here_error(include_here(here())) |
|
286 |
+ )}; |
|
287 |
+ if (!extension_enabled && included_by.empty()) |
|
288 |
+ throw std::runtime_error{STR( |
|
289 |
+ error << "; " << |
|
290 |
+ "#include found but #extension " << |
|
291 |
+ "GL_ARB_shading_language_include not enabled:\n" << |
|
292 |
+ here_error(include_here(here())) |
|
293 |
+ )}; |
|
294 |
+ |
|
295 |
+ // Process. |
|
296 |
+ auto source = std::string{}; |
|
297 |
+ if (included_by.end() == std::find( |
|
298 |
+ included_by.begin(), included_by.end(), here() |
|
299 |
+ )) |
|
300 |
+ { |
|
301 |
+ auto include_path_full = include_path; |
|
302 |
+ if (quote_open == "\"") |
|
303 |
+ { |
|
304 |
+ auto const pos = path.rfind('/'); |
|
305 |
+ if (pos != path.npos && pos != 0) |
|
306 |
+ include_path_full = STR( |
|
307 |
+ path.substr(0, pos + 1) << include_path |
|
308 |
+ ); |
|
309 |
+ } |
|
310 |
+ source = source_( |
|
311 |
+ error, |
|
312 |
+ include_path_full, root, |
|
313 |
+ defines, |
|
314 |
+ extension_behavior, |
|
315 |
+ include_here(here()) |
|
316 |
+ ); |
|
317 |
+ } |
|
318 |
+ |
|
319 |
+ // Output. |
|
320 |
+ ostream << source << "\n"; |
|
321 |
+ } |
|
322 |
+ |
|
166 | 323 |
// Non-processed line. |
167 | 324 |
else |
168 | 325 |
{ |
... | ... |
@@ -172,7 +329,7 @@ static std::string source_( |
172 | 329 |
} |
173 | 330 |
|
174 | 331 |
// Check for version. |
175 |
- if (!version_number) |
|
332 |
+ if (!version_number && included_by.empty()) |
|
176 | 333 |
throw std::runtime_error{STR( |
177 | 334 |
error << "; " << |
178 | 335 |
"found no #version." |
... | ... |
@@ -25,6 +25,7 @@ using Here = std::tuple<std::string, int, std::string>; |
25 | 25 |
// NOLINTNEXTLINE |
26 | 26 |
#define GLSHADER_INIT_(NAME, INIT) decltype(NAME) NAME INIT; |
27 | 27 |
GLSHADER_INIT_(Shader::root_, {}) |
28 |
+GLSHADER_INIT_(Shader::defines_, {}) |
|
28 | 29 |
|
29 | 30 |
|
30 | 31 |
template<typename Type> |
... | ... |
@@ -79,7 +80,8 @@ static void info_log_action_( |
79 | 80 |
static std::string source_( |
80 | 81 |
std::string const & error, |
81 | 82 |
std::string const & path, |
82 |
- std::string const & root |
|
83 |
+ std::string const & root, |
|
84 |
+ Shader::Defines const & defines |
|
83 | 85 |
) |
84 | 86 |
{ |
85 | 87 |
// Set here error. |
... | ... |
@@ -154,6 +156,11 @@ static std::string source_( |
154 | 156 |
|
155 | 157 |
// Output. |
156 | 158 |
ostream << line << "\n"; |
159 |
+ for (auto const & define : defines) |
|
160 |
+ ostream |
|
161 |
+ << "#define " |
|
162 |
+ << define.first << " " |
|
163 |
+ << define.second << "\n"; |
|
157 | 164 |
} |
158 | 165 |
|
159 | 166 |
// Non-processed line. |
... | ... |
@@ -263,7 +270,7 @@ Shader::Shader(Paths const & paths) |
263 | 270 |
|
264 | 271 |
// Set shader source. |
265 | 272 |
auto const source_error = STR("Failed to source " << shader_name); |
266 |
- auto const source = source_(source_error, path, root_); |
|
273 |
+ auto const source = source_(source_error, path, root_, defines_); |
|
267 | 274 |
auto const sources = std::array<char const *, 1>{{ |
268 | 275 |
source.c_str() |
269 | 276 |
}}; |
... | ... |
@@ -6,8 +6,11 @@ |
6 | 6 |
#include <cstring> |
7 | 7 |
#include <fstream> |
8 | 8 |
#include <memory> |
9 |
+#include <regex> |
|
10 |
+#include <sstream> |
|
9 | 11 |
#include <stdexcept> |
10 | 12 |
#include <string> |
13 |
+#include <tuple> |
|
11 | 14 |
#include <utility> |
12 | 15 |
#include <vector> |
13 | 16 |
|
... | ... |
@@ -16,6 +19,9 @@ |
16 | 19 |
#include <str.hpp> |
17 | 20 |
|
18 | 21 |
|
22 |
+using Here = std::tuple<std::string, int, std::string>; |
|
23 |
+ |
|
24 |
+ |
|
19 | 25 |
// NOLINTNEXTLINE |
20 | 26 |
#define GLSHADER_INIT_(NAME, INIT) decltype(NAME) NAME INIT; |
21 | 27 |
GLSHADER_INIT_(Shader::root_, {}) |
... | ... |
@@ -70,6 +76,106 @@ static void info_log_action_( |
70 | 76 |
} |
71 | 77 |
|
72 | 78 |
|
79 |
+static std::string source_( |
|
80 |
+ std::string const & error, |
|
81 |
+ std::string const & path, |
|
82 |
+ std::string const & root |
|
83 |
+) |
|
84 |
+{ |
|
85 |
+ // Set here error. |
|
86 |
+ auto const here_error = [](Here const & here) |
|
87 |
+ { |
|
88 |
+ return STR( |
|
89 |
+ std::get<0>(here) << ":" << |
|
90 |
+ std::get<1>(here) << ": " << |
|
91 |
+ std::get<2>(here) |
|
92 |
+ ); |
|
93 |
+ }; |
|
94 |
+ |
|
95 |
+ // Set full path. |
|
96 |
+ auto path_full = path; |
|
97 |
+ if (!root.empty()) |
|
98 |
+ path_full = STR(root << "/" << path_full); |
|
99 |
+ |
|
100 |
+ // Define and open input stream. |
|
101 |
+ auto istream = std::ifstream{path_full}; |
|
102 |
+ if (!istream) |
|
103 |
+ throw std::runtime_error{STR( |
|
104 |
+ error << "; " << |
|
105 |
+ "could not open file '" << path_full << "':\n" << |
|
106 |
+ std::strerror(errno) |
|
107 |
+ )}; |
|
108 |
+ |
|
109 |
+ // Define output stream. |
|
110 |
+ auto ostream = std::ostringstream{}; |
|
111 |
+ |
|
112 |
+ // Define parse regexes. |
|
113 |
+ static auto const re_ignored = std::regex{R"(\s*//.*$)"}; |
|
114 |
+ static auto const re_words = std::regex{R"((\w+(?:\s+\w+)*))"}; |
|
115 |
+ static auto const re_version = std::regex{R"(\s*#\s*version\s*(.*))"}; |
|
116 |
+ |
|
117 |
+ // Parse. |
|
118 |
+ auto version_number = 0; |
|
119 |
+ auto line_number = 0; |
|
120 |
+ auto line = std::string{}; |
|
121 |
+ auto match = std::smatch{}; |
|
122 |
+ auto here = [&]() |
|
123 |
+ { |
|
124 |
+ return Here{path_full, line_number, line}; |
|
125 |
+ }; |
|
126 |
+ while (++line_number, std::getline(istream, line)) |
|
127 |
+ { |
|
128 |
+ // Remove ignored. |
|
129 |
+ auto const content = std::regex_replace(line, re_ignored, ""); |
|
130 |
+ |
|
131 |
+ // Process version. |
|
132 |
+ if (std::regex_match(content, match, re_version)) |
|
133 |
+ { |
|
134 |
+ // Parse. |
|
135 |
+ auto const words = match.str(1); |
|
136 |
+ if (!std::regex_match(words, match, re_words)) |
|
137 |
+ throw std::runtime_error{STR( |
|
138 |
+ error << "; " << |
|
139 |
+ "malformed #version:\n" << |
|
140 |
+ here_error(here()) |
|
141 |
+ )}; |
|
142 |
+ auto const version = match.str(1); |
|
143 |
+ |
|
144 |
+ // Check for errors. |
|
145 |
+ if (version_number) |
|
146 |
+ throw std::runtime_error{STR( |
|
147 |
+ error << "; " << |
|
148 |
+ "found repeated #version:\n" << |
|
149 |
+ here_error(here()) |
|
150 |
+ )}; |
|
151 |
+ |
|
152 |
+ // Process. |
|
153 |
+ version_number = std::stoi(version); |
|
154 |
+ |
|
155 |
+ // Output. |
|
156 |
+ ostream << line << "\n"; |
|
157 |
+ } |
|
158 |
+ |
|
159 |
+ // Non-processed line. |
|
160 |
+ else |
|
161 |
+ { |
|
162 |
+ // Output. |
|
163 |
+ ostream << line << "\n"; |
|
164 |
+ } |
|
165 |
+ } |
|
166 |
+ |
|
167 |
+ // Check for version. |
|
168 |
+ if (!version_number) |
|
169 |
+ throw std::runtime_error{STR( |
|
170 |
+ error << "; " << |
|
171 |
+ "found no #version." |
|
172 |
+ )}; |
|
173 |
+ |
|
174 |
+ // Return. |
|
175 |
+ return ostream.str(); |
|
176 |
+} |
|
177 |
+ |
|
178 |
+ |
|
73 | 179 |
Shader::Shader(Paths const & paths) |
74 | 180 |
: |
75 | 181 |
program_{0}, |
... | ... |
@@ -157,17 +263,7 @@ Shader::Shader(Paths const & paths) |
157 | 263 |
|
158 | 264 |
// Set shader source. |
159 | 265 |
auto const source_error = STR("Failed to source " << shader_name); |
160 |
- auto path_full = path; |
|
161 |
- if (!root_.empty()) |
|
162 |
- path_full = STR(root_ << "/" << path_full); |
|
163 |
- auto source_istream = std::ifstream{path_full}; |
|
164 |
- if (!source_istream) |
|
165 |
- throw std::runtime_error{STR( |
|
166 |
- source_error << "; " << |
|
167 |
- "could not open file '" << path_full << "':\n" << |
|
168 |
- std::strerror(errno) |
|
169 |
- )}; |
|
170 |
- auto const source = STR(source_istream.rdbuf()); |
|
266 |
+ auto const source = source_(source_error, path, root_); |
|
171 | 267 |
auto const sources = std::array<char const *, 1>{{ |
172 | 268 |
source.c_str() |
173 | 269 |
}}; |
... | ... |
@@ -229,4 +229,18 @@ void Shader::validate_() const |
229 | 229 |
glValidateProgram, program_, |
230 | 230 |
GL_VALIDATE_STATUS, glGetProgramiv, glGetProgramInfoLog |
231 | 231 |
); |
232 |
+ |
|
233 |
+ // Assert current. |
|
234 |
+ current_(validate_error); |
|
235 |
+} |
|
236 |
+ |
|
237 |
+ |
|
238 |
+void Shader::current_(std::string const & error) const |
|
239 |
+{ |
|
240 |
+ // Error if not current. |
|
241 |
+ if (get_integer_<GLuint>(GL_CURRENT_PROGRAM) != program_) |
|
242 |
+ throw std::runtime_error{STR( |
|
243 |
+ error << "; " << |
|
244 |
+ "shader program not current." |
|
245 |
+ )}; |
|
232 | 246 |
} |
... | ... |
@@ -216,3 +216,17 @@ Shader::~Shader() |
216 | 216 |
if (program_) |
217 | 217 |
glDeleteProgram(program_); |
218 | 218 |
} |
219 |
+ |
|
220 |
+ |
|
221 |
+void Shader::validate_() const |
|
222 |
+{ |
|
223 |
+ // Set error. |
|
224 |
+ auto const validate_error = STR("Failed to validate " << program_name_); |
|
225 |
+ |
|
226 |
+ // Validate program. |
|
227 |
+ info_log_action_( |
|
228 |
+ validate_error, |
|
229 |
+ glValidateProgram, program_, |
|
230 |
+ GL_VALIDATE_STATUS, glGetProgramiv, glGetProgramInfoLog |
|
231 |
+ ); |
|
232 |
+} |
... | ... |
@@ -0,0 +1,218 @@ |
1 |
+#include <glshader.hpp> |
|
2 |
+ |
|
3 |
+#include <algorithm> |
|
4 |
+#include <array> |
|
5 |
+#include <cerrno> |
|
6 |
+#include <cstring> |
|
7 |
+#include <fstream> |
|
8 |
+#include <memory> |
|
9 |
+#include <stdexcept> |
|
10 |
+#include <string> |
|
11 |
+#include <utility> |
|
12 |
+#include <vector> |
|
13 |
+ |
|
14 |
+#include <GL/glew.h> |
|
15 |
+ |
|
16 |
+#include <str.hpp> |
|
17 |
+ |
|
18 |
+ |
|
19 |
+// NOLINTNEXTLINE |
|
20 |
+#define GLSHADER_INIT_(NAME, INIT) decltype(NAME) NAME INIT; |
|
21 |
+GLSHADER_INIT_(Shader::root_, {}) |
|
22 |
+ |
|
23 |
+ |
|
24 |
+template<typename Type> |
|
25 |
+static Type get_integer_(GLenum name, bool supported = true) |
|
26 |
+{ |
|
27 |
+ auto data = GLint{}; |
|
28 |
+ if (supported) |
|
29 |
+ glGetIntegerv(name, &data); |
|
30 |
+ return (Type)data; |
|
31 |
+} |
|
32 |
+ |
|
33 |
+ |
|
34 |
+static void info_log_action_( |
|
35 |
+ std::string const & error, |
|
36 |
+ void (action)(GLuint object), |
|
37 |
+ GLuint object, |
|
38 |
+ GLenum status_enum, |
|
39 |
+ void (GLAPIENTRY * getObjectiv)( |
|
40 |
+ GLuint object, GLenum pname, GLint * params |
|
41 |
+ ), |
|
42 |
+ void (GLAPIENTRY * getObjectInfoLog)( |
|
43 |
+ GLuint object, GLsizei max_length, GLsizei * length, GLchar * info_log |
|
44 |
+ ) |
|
45 |
+) |
|
46 |
+{ |
|
47 |
+ // Perform action. |
|
48 |
+ action(object); |
|
49 |
+ |
|
50 |
+ // Check status. |
|
51 |
+ auto status = GLint{}; |
|
52 |
+ getObjectiv(object, status_enum, &status); |
|
53 |
+ if (status) |
|
54 |
+ return; |
|
55 |
+ |
|
56 |
+ // Get info log length. |
|
57 |
+ auto info_log_length = GLint{}; |
|
58 |
+ getObjectiv(object, GL_INFO_LOG_LENGTH, &info_log_length); |
|
59 |
+ |
|
60 |
+ // Get info log content. |
|
61 |
+ // NOLINTNEXTLINE |
|
62 |
+ auto info_log = std::unique_ptr<GLchar[]>(new GLchar[info_log_length]); |
|
63 |
+ if (info_log_length) |
|
64 |
+ getObjectInfoLog(object, info_log_length, nullptr, &info_log[0]); |
|
65 |
+ |
|
66 |
+ // Throw. |
|
67 |
+ throw std::runtime_error{STR( |
|
68 |
+ error << (!info_log_length ? "." : STR(":\n" << &info_log[0])) |
|
69 |
+ )}; |
|
70 |
+} |
|
71 |
+ |
|
72 |
+ |
|
73 |
+Shader::Shader(Paths const & paths) |
|
74 |
+: |
|
75 |
+ program_{0}, |
|
76 |
+ program_name_{STR( |
|
77 |
+ "shader program " << STR_JOIN(", ", it, "'" << it << "'", paths) |
|
78 |
+ )} |
|
79 |
+{ |
|
80 |
+ // Get label limits. |
|
81 |
+ static auto const max_label_length = get_integer_<GLsizei>( |
|
82 |
+ GL_MAX_LABEL_LENGTH, GLEW_VERSION_4_3 || GLEW_KHR_debug |
|
83 |
+ ); |
|
84 |
+ |
|
85 |
+ try |
|
86 |
+ { |
|
87 |
+ // Create program. |
|
88 |
+ program_ = glCreateProgram(); |
|
89 |
+ if (!program_) |
|
90 |
+ throw std::runtime_error{STR( |
|
91 |
+ "Failed to create " << program_name_ << "." |
|
92 |
+ )}; |
|
93 |
+ |
|
94 |
+ // Label program. |
|
95 |
+ if (GLEW_VERSION_4_3 || GLEW_KHR_debug) |
|
96 |
+ glObjectLabel( |
|
97 |
+ GL_PROGRAM, |
|
98 |
+ program_, |
|
99 |
+ std::min(max_label_length, (GLsizei)program_name_.length()), |
|
100 |
+ program_name_.c_str() |
|
101 |
+ ); |
|
102 |
+ |
|
103 |
+ // Process shader paths. |
|
104 |
+ auto shaders = std::vector<GLuint>{}; |
|
105 |
+ shaders.reserve(paths.size()); |
|
106 |
+ for (auto const & path : paths) |
|
107 |
+ { |
|
108 |
+ // Set shader name. |
|
109 |
+ auto const shader_name = STR( |
|
110 |
+ "shader '" << path << "' of " << program_name_ |
|
111 |
+ ); |
|
112 |
+ |
|
113 |
+ // Infer shader type from path extension. |
|
114 |
+ auto const type_error = STR( |
|
115 |
+ "Failed to infer type of " << shader_name |
|
116 |
+ ); |
|
117 |
+ auto const type_pos = path.rfind('.'); |
|
118 |
+ if (type_pos == path.npos) |
|
119 |
+ throw std::runtime_error{STR( |
|
120 |
+ type_error << "; " << |
|
121 |
+ "no file extension." |
|
122 |
+ )}; |
|
123 |
+ auto const type_name = path.substr(type_pos + 1); |
|
124 |
+ auto const type = |
|
125 |
+ type_name == "vert" ? GL_VERTEX_SHADER : |
|
126 |
+ type_name == "tesc" ? GL_TESS_CONTROL_SHADER : |
|
127 |
+ type_name == "tese" ? GL_TESS_EVALUATION_SHADER : |
|
128 |
+ type_name == "geom" ? GL_GEOMETRY_SHADER : |
|
129 |
+ type_name == "frag" ? GL_FRAGMENT_SHADER : |
|
130 |
+ type_name == "comp" ? GL_COMPUTE_SHADER : |
|
131 |
+ GLenum{0}; |
|
132 |
+ if (!type) |
|
133 |
+ throw std::runtime_error{STR( |
|
134 |
+ type_error << "; " << |
|
135 |
+ "unknown file extension '" << type_name << "'." |
|
136 |
+ )}; |
|
137 |
+ |
|
138 |
+ // Create, attach, and flag shader for deletion when detached. |
|
139 |
+ auto const shader = glCreateShader(type); |
|
140 |
+ if (!shader) |
|
141 |
+ throw std::runtime_error{STR( |
|
142 |
+ "Failed to create " << type_name << " shader for " << |
|
143 |
+ shader_name << "." |
|
144 |
+ )}; |
|
145 |
+ shaders.push_back(shader); |
|
146 |
+ glAttachShader(program_, shader); |
|
147 |
+ glDeleteShader(shader); |
|
148 |
+ |
|
149 |
+ // Label shader. |
|
150 |
+ if (GLEW_VERSION_4_3 || GLEW_KHR_debug) |
|
151 |
+ glObjectLabel( |
|
152 |
+ GL_SHADER, |
|
153 |
+ shader, |
|
154 |
+ std::min(max_label_length, (GLsizei)shader_name.length()), |
|
155 |
+ shader_name.c_str() |
|
156 |
+ ); |
|
157 |
+ |
|
158 |
+ // Set shader source. |
|
159 |
+ auto const source_error = STR("Failed to source " << shader_name); |
|
160 |
+ auto path_full = path; |
|
161 |
+ if (!root_.empty()) |
|
162 |
+ path_full = STR(root_ << "/" << path_full); |
|
163 |
+ auto source_istream = std::ifstream{path_full}; |
|
164 |
+ if (!source_istream) |
|
165 |
+ throw std::runtime_error{STR( |
|
166 |
+ source_error << "; " << |
|
167 |
+ "could not open file '" << path_full << "':\n" << |
|
168 |
+ std::strerror(errno) |
|
169 |
+ )}; |
|
170 |
+ auto const source = STR(source_istream.rdbuf()); |
|
171 |
+ auto const sources = std::array<char const *, 1>{{ |
|
172 |
+ source.c_str() |
|
173 |
+ }}; |
|
174 |
+ glShaderSource(shader, sources.size(), &sources[0], nullptr); |
|
175 |
+ |
|
176 |
+ // Compile shader. |
|
177 |
+ info_log_action_( |
|
178 |
+ STR("Failed to compile " << shader_name), |
|
179 |
+ glCompileShader, shader, |
|
180 |
+ GL_COMPILE_STATUS, glGetShaderiv, glGetShaderInfoLog |
|
181 |
+ ); |
|
182 |
+ } |
|
183 |
+ |
|
184 |
+ // Link program. |
|
185 |
+ info_log_action_( |
|
186 |
+ STR("Failed to link " << program_name_), |
|
187 |
+ glLinkProgram, program_, |
|
188 |
+ GL_LINK_STATUS, glGetProgramiv, glGetProgramInfoLog |
|
189 |
+ ); |
|
190 |
+ |
|
191 |
+ // Detach shaders. |
|
192 |
+ for (auto const & shader : shaders) |
|
193 |
+ glDetachShader(program_, shader); |
|
194 |
+ } |
|
195 |
+ catch (...) |
|
196 |
+ { |
|
197 |
+ // Delete program (and detach and delete shaders). |
|
198 |
+ if (program_) |
|
199 |
+ glDeleteProgram(program_); |
|
200 |
+ throw; |
|
201 |
+ } |
|
202 |
+} |
|
203 |
+ |
|
204 |
+ |
|
205 |
+Shader::Shader(Shader && other) noexcept |
|
206 |
+: |
|
207 |
+ program_ {other.program_}, |
|
208 |
+ program_name_{std::move(other.program_name_)} |
|
209 |
+{ |
|
210 |
+ other.program_ = 0; |
|
211 |
+} |
|
212 |
+ |
|
213 |
+ |
|
214 |
+Shader::~Shader() |
|
215 |
+{ |
|
216 |
+ if (program_) |
|
217 |
+ glDeleteProgram(program_); |
|
218 |
+} |