]> git.tdb.fi Git - libs/gl.git/commitdiff
Add tessellation shader support to the engine
authorMikko Rasa <tdb@tdb.fi>
Sat, 3 Sep 2022 13:38:03 +0000 (16:38 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 3 Sep 2022 13:38:44 +0000 (16:38 +0300)
21 files changed:
extensions/arb_tessellation_shader.glext [new file with mode: 0644]
source/backends/opengl/batch_backend.cpp
source/backends/opengl/pipelinestate_backend.cpp
source/backends/opengl/primitivetype_backend.cpp
source/backends/opengl/program_backend.cpp
source/backends/opengl/program_backend.h
source/backends/vulkan/device_backend.cpp
source/backends/vulkan/module_backend.cpp
source/backends/vulkan/pipelinestate_backend.cpp
source/backends/vulkan/primitivetype_backend.cpp
source/backends/vulkan/program_backend.cpp
source/backends/vulkan/program_backend.h
source/core/batch.cpp
source/core/batch.h
source/core/module.h
source/core/pipelinestate.cpp
source/core/pipelinestate.h
source/core/primitivetype.cpp
source/core/primitivetype.h
source/core/program.h
source/render/renderer.cpp

diff --git a/extensions/arb_tessellation_shader.glext b/extensions/arb_tessellation_shader.glext
new file mode 100644 (file)
index 0000000..7fb6202
--- /dev/null
@@ -0,0 +1 @@
+extension ARB_tessellation_shader
index d80c11cc84fef0d5926041cd925c1e45b5d2ca49..2bd3c8374b3278e20e01e475e74e82a862b27dad 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/gl/extensions/arb_tessellation_shader.h>
 #include <msp/gl/extensions/msp_primitive_restart.h>
 #include "batch_backend.h"
 
@@ -7,7 +8,10 @@ namespace GL {
 OpenGLBatch::OpenGLBatch(PrimitiveType t):
        gl_prim_type(get_gl_primitive_type(t)),
        gl_index_type(GL_UNSIGNED_SHORT)
-{ }
+{
+       if(t==PATCHES)
+               static Require _req(ARB_tessellation_shader);
+}
 
 bool OpenGLBatch::check_restart(bool require)
 {
index 8ffa47fcda75d8ea44e9ff9e74673ca54f43a21a..204e8139f0fc70802aaf3592b68a60779a124bae 100644 (file)
@@ -4,6 +4,7 @@
 #include <msp/gl/extensions/arb_shader_objects.h>
 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
 #include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_tessellation_shader.h>
 #include <msp/gl/extensions/ext_framebuffer_object.h>
 #include <msp/gl/extensions/msp_primitive_restart.h>
 #include "blend.h"
@@ -180,6 +181,13 @@ void OpenGLPipelineState::apply() const
                }
        }
 
+       if(changes&PipelineState::PATCH_SIZE)
+               if(self.patch_size)
+               {
+                       static Require _req(ARB_tessellation_shader);
+                       glPatchParameteri(GL_PATCH_VERTICES, self.patch_size);
+               }
+
        if(changes&PipelineState::FACE_CULL)
        {
                glFrontFace(self.front_face==CLOCKWISE ? GL_CW : GL_CCW);
index 42ee9f26ac73196d50a915825d549f92d07db936..a3c862c020913a7ce0b11016dcb1962491a1ba34 100644 (file)
@@ -17,6 +17,7 @@ unsigned get_gl_primitive_type(PrimitiveType pt)
        case TRIANGLES: return GL_TRIANGLES;
        case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
        case TRIANGLE_FAN: return GL_TRIANGLE_FAN;
+       case PATCHES: return GL_PATCHES;
        default: throw invalid_argument("get_gl_primitive_type");
        }
 }
index 398d35ce7a6c364aba56634cb7aa0be67ad174d6..3ebb7da1292d8de854a09131a3eb3ece18eb127b 100644 (file)
@@ -8,6 +8,7 @@
 #include <msp/gl/extensions/arb_separate_shader_objects.h>
 #include <msp/gl/extensions/arb_shader_objects.h>
 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_tessellation_shader.h>
 #include <msp/gl/extensions/arb_vertex_shader.h>
 #include <msp/gl/extensions/ext_gpu_shader4.h>
 #include <msp/gl/extensions/khr_debug.h>
@@ -80,6 +81,8 @@ unsigned OpenGLProgram::add_stage(Stage type)
        switch(type)
        {
        case VERTEX: { static Require _req(ARB_vertex_shader); gl_type = GL_VERTEX_SHADER; } break;
+       case TESS_CONTROL: { static Require _req(ARB_tessellation_shader); gl_type = GL_TESS_CONTROL_SHADER; } break;
+       case TESS_EVAL: { static Require _req(ARB_tessellation_shader); gl_type = GL_TESS_EVALUATION_SHADER; } break;
        case GEOMETRY: { static Require _req(ARB_geometry_shader4); gl_type = GL_GEOMETRY_SHADER; } break;
        case FRAGMENT: { static Require _req(ARB_fragment_shader); gl_type = GL_FRAGMENT_SHADER; } break;
        case COMPUTE: { static Require _req(ARB_compute_shader); gl_type = GL_COMPUTE_SHADER; } break;
@@ -123,6 +126,8 @@ void OpenGLProgram::add_glsl_stages(const GlslModule &mod, const map<string, int
                switch(st)
                {
                case SL::Stage::VERTEX: stage_id = add_stage(VERTEX); break;
+               case SL::Stage::TESS_CONTROL: stage_id = add_stage(TESS_CONTROL); break;
+               case SL::Stage::TESS_EVAL: stage_id = add_stage(TESS_EVAL); break;
                case SL::Stage::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
                case SL::Stage::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
                case SL::Stage::COMPUTE: stage_id = add_stage(COMPUTE); break;
@@ -229,6 +234,8 @@ void OpenGLProgram::add_spirv_stages(const SpirVModule &mod, const map<string, i
                switch(e.stage)
                {
                case SpirVModule::VERTEX: stage_id = add_stage(VERTEX); break;
+               case SpirVModule::TESS_CONTROL: stage_id = add_stage(TESS_CONTROL); break;
+               case SpirVModule::TESS_EVAL: stage_id = add_stage(TESS_EVAL); break;
                case SpirVModule::GEOMETRY: stage_id = add_stage(GEOMETRY); break;
                case SpirVModule::FRAGMENT: stage_id = add_stage(FRAGMENT); break;
                case SpirVModule::COMPUTE: stage_id = add_stage(COMPUTE); break;
@@ -543,7 +550,7 @@ void OpenGLProgram::set_debug_name(const string &name)
 void OpenGLProgram::set_stage_debug_name(unsigned stage_id, Stage type)
 {
 #ifdef DEBUG
-       static const char *const suffixes[] = { " [VS]", " [GS]", " [FS]", " [CS]" };
+       static const char *const suffixes[] = { " [VS]", " [TCS]", " [TES]", " [GS]", " [FS]", " [CS]" };
        string name = debug_name+suffixes[type];
        glObjectLabel(GL_SHADER, stage_id, name.size(), name.c_str());
 #else
index 1a548ed69116d66d96bdd28e5194abb1cd441dfd..9cce0f7e187ca4390e86962059955de5e1451c4f 100644 (file)
@@ -18,6 +18,8 @@ protected:
        enum Stage
        {
                VERTEX,
+               TESS_CONTROL,
+               TESS_EVAL,
                GEOMETRY,
                FRAGMENT,
                COMPUTE,
@@ -58,6 +60,7 @@ protected:
        void finalize_uniforms();
 
        bool is_compute() const { return stage_ids[COMPUTE]; }
+       bool has_tessellation() const { return stage_ids[TESS_CONTROL] && stage_ids[TESS_EVAL]; }
 
        void set_debug_name(const std::string &);
        void set_stage_debug_name(unsigned, Stage);
index 9a8b930d713f6d100f3351a0a91040232c3f19f7..6d8995807c842832e22dce16315ca80a0c8bd5fd 100644 (file)
@@ -31,6 +31,7 @@ Graphics::VulkanOptions VulkanDevice::create_default_options()
 {
        Graphics::VulkanOptions opts;
        opts.enable_geometry_shader = true;
+       opts.enable_tessellation_shader = true;
 #ifdef DEBUG
        const char *disable_ptr = getenv("MSPGL_DISABLE_VALIDATION");
        if(disable_ptr && *disable_ptr)
index 492de9a6311ac9ac5d00207724fba93b1c47d32e..b0c39989a19295861e64e6ae90b0f7783e428702 100644 (file)
@@ -75,6 +75,8 @@ unsigned get_vulkan_stage(unsigned stage)
        switch(stage)
        {
        case SpirVModule::VERTEX: return VK_SHADER_STAGE_VERTEX_BIT;
+       case SpirVModule::TESS_CONTROL: return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+       case SpirVModule::TESS_EVAL: return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
        case SpirVModule::GEOMETRY: return VK_SHADER_STAGE_GEOMETRY_BIT;
        case SpirVModule::FRAGMENT: return VK_SHADER_STAGE_FRAGMENT_BIT;
        case SpirVModule::COMPUTE: return VK_SHADER_STAGE_COMPUTE_BIT;
index ab5d3dd4cd8ca10a20342eb1b063a0c29c92b8bf..f06e2d324a82e9302135b46c49d7401b9c008f4a 100644 (file)
@@ -49,7 +49,8 @@ void VulkanPipelineState::update() const
        }
 
        constexpr unsigned graphics_mask = PipelineState::FRAMEBUFFER|PipelineState::VERTEX_SETUP|PipelineState::FACE_CULL|
-               PipelineState::DEPTH_TEST|PipelineState::STENCIL_TEST|PipelineState::BLEND|PipelineState::PRIMITIVE_TYPE;
+               PipelineState::DEPTH_TEST|PipelineState::STENCIL_TEST|PipelineState::BLEND|PipelineState::PRIMITIVE_TYPE|
+               PipelineState::PATCH_SIZE;
        unsigned pipeline_mask = PipelineState::SHPROG;
        if(!self.shprog->is_compute())
                pipeline_mask |= graphics_mask;
@@ -177,9 +178,13 @@ void VulkanPipelineState::fill_graphics_creation_info(vector<char> &buffer) cons
                        ++n_color_attachments;
        }
 
-       StructureBuilder sb(buffer, 10);
+       bool has_tessellation = (self.shprog && self.shprog->has_tessellation());
+
+       StructureBuilder sb(buffer, 12);
        VkGraphicsPipelineCreateInfo *const &pipeline_info = sb.add<VkGraphicsPipelineCreateInfo>();
        VkPipelineInputAssemblyStateCreateInfo *const &input_assembly_info = sb.add<VkPipelineInputAssemblyStateCreateInfo>();
+       VkPipelineTessellationStateCreateInfo *const &tessellation_info = sb.add<VkPipelineTessellationStateCreateInfo>(has_tessellation);
+       VkPipelineTessellationDomainOriginStateCreateInfo *const &tess_origin_info = sb.add<VkPipelineTessellationDomainOriginStateCreateInfo>(has_tessellation);
        VkPipelineViewportStateCreateInfo *const &viewport_info = sb.add<VkPipelineViewportStateCreateInfo>();
        VkPipelineRasterizationStateCreateInfo *const &raster_info = sb.add<VkPipelineRasterizationStateCreateInfo>();
        VkPipelineMultisampleStateCreateInfo *const &multisample_info = sb.add<VkPipelineMultisampleStateCreateInfo>();
@@ -191,7 +196,17 @@ void VulkanPipelineState::fill_graphics_creation_info(vector<char> &buffer) cons
 
        input_assembly_info->sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
        input_assembly_info->topology = static_cast<VkPrimitiveTopology>(get_vulkan_primitive_type(self.primitive_type));
-       input_assembly_info->primitiveRestartEnable = true;
+       input_assembly_info->primitiveRestartEnable = !has_tessellation;
+
+       if(has_tessellation)
+       {
+               tessellation_info->sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
+               tessellation_info->pNext = tess_origin_info;
+               tessellation_info->patchControlPoints = self.patch_size;
+
+               tess_origin_info->sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO;
+               tess_origin_info->domainOrigin = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
+       }
 
        viewport_info->sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
        viewport_info->viewportCount = 1;
@@ -264,7 +279,7 @@ void VulkanPipelineState::fill_graphics_creation_info(vector<char> &buffer) cons
        pipeline_info->sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
 
        pipeline_info->pInputAssemblyState = input_assembly_info;
-       pipeline_info->pTessellationState = 0;
+       pipeline_info->pTessellationState = (has_tessellation ? tessellation_info : 0);
        pipeline_info->pViewportState = viewport_info;
        pipeline_info->pRasterizationState = raster_info;
        pipeline_info->pMultisampleState = multisample_info;
index 4115af446a6f855fbcc743839a6e5e6886dab4d4..8538eadb0185a4cacba378193a546325ff91e68e 100644 (file)
@@ -17,6 +17,7 @@ unsigned get_vulkan_primitive_type(PrimitiveType pt)
        case TRIANGLES: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
        case TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
        case TRIANGLE_FAN: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
+       case PATCHES: return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
        default: throw invalid_argument("get_vulkan_primitive_type");
        }
 }
index 993a3e38ef965d11b838fb9b41653cb2fe5b3b29..10c04d83e7eabe6c6cb3cd7b79f5dfbc84ee7b34 100644 (file)
@@ -180,6 +180,12 @@ bool VulkanProgram::is_compute() const
        return stage_flags&VK_SHADER_STAGE_COMPUTE_BIT;
 }
 
+bool VulkanProgram::has_tessellation() const
+{
+       constexpr unsigned tess_flags = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+       return (stage_flags&tess_flags)==tess_flags;
+}
+
 void VulkanProgram::set_debug_name(const string &name)
 {
 #ifdef DEBUG
index 0e48b14bcb9aa5371e0fcdf0b077006d0262fa01..654b19c73f0bffc444940f71126fcb8a197ca984 100644 (file)
@@ -36,6 +36,7 @@ protected:
        void finalize_uniforms();
 
        bool is_compute() const;
+       bool has_tessellation() const;
 
        void set_debug_name(const std::string &);
        void set_vulkan_object_name() const;
index c68beab908f8dc2a07c699a8b7bd6a9e41b5f35c..83b7d49b53b69885799a03bfea9e9f33cbb7b4f4 100644 (file)
@@ -73,6 +73,16 @@ void Batch::set_index_type(DataType t)
        mark_dirty();
 }
 
+void Batch::set_patch_size(unsigned s)
+{
+       if(prim_type!=PATCHES)
+               throw invalid_operation("Batch::set_patch_size");
+       if(s<1)
+               throw invalid_argument("Batch::set_patch_size");
+
+       patch_size = s;
+}
+
 Batch &Batch::append(unsigned i)
 {
        append_index(i);
@@ -176,6 +186,7 @@ Batch::Loader::Loader(Batch &b):
        DataFile::ObjectLoader<Batch>(b)
 {
        add("indices", &Loader::indices);
+       add("patch_size", &Loader::patch_size);
 }
 
 void Batch::Loader::indices(const vector<unsigned> &ind)
@@ -183,5 +194,10 @@ void Batch::Loader::indices(const vector<unsigned> &ind)
        obj.append(ind);
 }
 
+void Batch::Loader::patch_size(unsigned s)
+{
+       obj.set_patch_size(s);
+}
+
 } // namespace GL
 } // namespace Msp
index 1fc225210f739a457278b7e5f6ba3ba87e62b4da..f3495860e4b4297d072869f73082ce6f4e6a7c7b 100644 (file)
@@ -30,6 +30,7 @@ public:
                Loader(Batch &);
        private:
                void indices(const std::vector<unsigned> &);
+               void patch_size(unsigned);
        };
 
 private:
@@ -37,6 +38,7 @@ private:
        DataType index_type;
        std::vector<std::uint8_t> data;
        unsigned max_index;
+       unsigned patch_size = 3;
 
 public:
        Batch(PrimitiveType);
@@ -50,6 +52,12 @@ public:
 
        DataType get_index_type() const { return index_type; }
 
+       /** Sets the number of control points per patch.  Only available if the
+       primitive type is PATCHES. */
+       void set_patch_size(unsigned);
+
+       unsigned get_patch_size() const { return patch_size; }
+
        /** Appends a single index.  The data type is automatically adjusted if the
        index is too large for the current data type. */
        Batch &append(unsigned);
index 480a0669ffbb6275324a7ab2b3c5bb660fdba2eb..4d1511121e4191165bd3c9c13b7fb5f866c3e54b 100644 (file)
@@ -103,6 +103,8 @@ public:
        enum Stage
        {
                VERTEX = 0,
+               TESS_CONTROL = 1,
+               TESS_EVAL = 2,
                GEOMETRY = 3,
                FRAGMENT = 4,
                COMPUTE = 5
index 4dc54e2403ffea9288375df8dbfa09eb01f50283..0769d441a83a3de178f932af44142bf2b81c1f73 100644 (file)
@@ -107,6 +107,11 @@ void PipelineState::set_primitive_type(PrimitiveType t)
        set(primitive_type, t, PRIMITIVE_TYPE);
 }
 
+void PipelineState::set_patch_size(unsigned s)
+{
+       set(patch_size, s, PATCH_SIZE);
+}
+
 void PipelineState::set_front_face(FaceWinding w)
 {
        set(front_face, w, FACE_CULL);
index 965bc15f16f11e5dbcfef30068dad621cee0a3a3..5156e0b5e94119826fb1e6cab79fe6b26c9a5b1e 100644 (file)
@@ -73,7 +73,8 @@ private:
                DEPTH_TEST = 256,
                STENCIL_TEST = 512,
                BLEND = 1024,
-               PRIMITIVE_TYPE = 2048
+               PRIMITIVE_TYPE = 2048,
+               PATCH_SIZE = 4096
        };
 
        const Framebuffer *framebuffer = 0;
@@ -83,6 +84,7 @@ private:
        std::vector<BoundResource> resources;
        const VertexSetup *vertex_setup = 0;
        PrimitiveType primitive_type = TRIANGLES;
+       unsigned patch_size = 0;
        FaceWinding front_face = COUNTERCLOCKWISE;
        CullMode face_cull = NO_CULL;
        DepthTest depth_test;
@@ -105,6 +107,7 @@ private:
 public:
        void set_vertex_setup(const VertexSetup *);
        void set_primitive_type(PrimitiveType);
+       void set_patch_size(unsigned);
        void set_front_face(FaceWinding);
        void set_face_cull(CullMode);
        void set_depth_test(const DepthTest &);
index 7698e2d65e0fcf916cfb70d4ac228e60830a1f97..ed4b154561aa0b276d5010d5d7508a27dfe82002 100644 (file)
@@ -20,6 +20,8 @@ void operator>>(const LexicalConverter &conv, PrimitiveType &pt)
                pt = TRIANGLE_STRIP;
        else if(conv.get()=="TRIANGLE_FAN")
                pt = TRIANGLE_FAN;
+       else if(conv.get()=="PATCHES")
+               pt = PATCHES;
        else
                throw lexical_error(format("conversion of '%s' to PrimitiveType", conv.get()));
 }
index 0c566f895b6d5cbd02a3b74060729637520322f0..ae9be607e006d4fc742bff379ce3716239ab0e22 100644 (file)
@@ -13,7 +13,8 @@ enum PrimitiveType
        LINE_STRIP,
        TRIANGLES,
        TRIANGLE_STRIP,
-       TRIANGLE_FAN
+       TRIANGLE_FAN,
+       PATCHES
 };
 
 void operator>>(const LexicalConverter &, PrimitiveType &);
index c652f468b00cd3855a1b3729a358895dac998c80..3e206a70e3c06c9fffe58fe4fd20f745f07be769 100644 (file)
@@ -73,6 +73,7 @@ private:
 
 public:
        using ProgramBackend::is_compute;
+       using ProgramBackend::has_tessellation;
 
        ReflectData::LayoutHash get_uniform_layout_hash() const { return reflect_data.layout_hash; }
        unsigned get_n_descriptor_sets() const { return reflect_data.n_descriptor_sets; }
index aab37b6796bece1dc7d2b58e1018fc51f1666a78..352cf4201f17a372e044532f395069483eb14975 100644 (file)
@@ -295,6 +295,8 @@ void Renderer::apply_batch(const Batch &batch)
        batch.refresh(frame_index);
        PipelineState &ps = get_pipeline_state();
        ps.set_primitive_type(batch.get_type());
+       if(batch.get_type()==PATCHES)
+               ps.set_patch_size(batch.get_patch_size());
        commands.use_pipeline(&ps);
 }