Browse code

Add texture

Robert Cranston authored on 01/07/2021 00:25:58
Showing 1 changed files
... ...
@@ -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
+}
Browse code

Add uniform block / buffer

Robert Cranston authored on 17/05/2020 15:29:35
Showing 1 changed files
... ...
@@ -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
+}
Browse code

Add uniform

Robert Cranston authored on 21/05/2021 00:46:36
Showing 1 changed files
... ...
@@ -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
+}
Browse code

Add input and output

Robert Cranston authored on 11/05/2021 12:27:29
Showing 1 changed files
... ...
@@ -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
     {
Browse code

Add preprocessing include

Robert Cranston authored on 26/02/2021 16:36:35
Showing 1 changed files
... ...
@@ -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."
Browse code

Add preprocessing defines

Robert Cranston authored on 26/02/2021 23:38:21
Showing 1 changed files
... ...
@@ -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
             }};
Browse code

Add preprocessing version

Robert Cranston authored on 26/02/2021 16:06:29
Showing 1 changed files
... ...
@@ -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
             }};
Browse code

Add use

Robert Cranston authored on 27/02/2021 00:53:41
Showing 1 changed files
... ...
@@ -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
 }
Browse code

Add validate

Robert Cranston authored on 27/02/2021 00:19:43
Showing 1 changed files
... ...
@@ -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
+}
Browse code

Add create

Robert Cranston authored on 16/05/2020 11:49:30
Showing 1 changed files
... ...
@@ -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
+}
Browse code

Add project

Robert Cranston authored on 16/05/2020 11:47:34
Showing 1 changed files
1 1
new file mode 100644