]> git.tdb.fi Git - libs/gl.git/commitdiff
Support multiple PipelineStates in Renderer
authorMikko Rasa <tdb@tdb.fi>
Mon, 17 Jan 2022 20:41:24 +0000 (22:41 +0200)
committerMikko Rasa <tdb@tdb.fi>
Mon, 17 Jan 2022 20:41:24 +0000 (22:41 +0200)
In the Vulkan backend it can be beneficial to keep separate pipeline
states and avoid having to repeatedly compute hashes and look them up
from the cache.  The downside is that every time the used state object
changes, shader data has to be reapplied.  But it's likely this would
be necessary anyway due to different shader or data being used.

source/backends/opengl/renderer_backend.h
source/backends/vulkan/renderer_backend.cpp
source/backends/vulkan/renderer_backend.h
source/render/renderer.cpp
source/render/renderer.h

index 4adcce3a3b14579f1e5e24f175878d66ecd190f8..685b6464286e767c1837f47bc3e08a9a0fdcf083 100644 (file)
@@ -14,6 +14,9 @@ protected:
 
        void begin() { }
        void end();
+
+       void set_pipeline_key(std::uintptr_t) { }
+       PipelineState &get_pipeline_state() { return pipeline_state; }
 };
 
 using RendererBackend = OpenGLRenderer;
index ab77158a1d52e3cf6d8acf6d25bbbe78998fb669..3b9281b5c609b8874539eac1f0b5467accd41464 100644 (file)
@@ -4,6 +4,10 @@
 namespace Msp {
 namespace GL {
 
+VulkanRenderer::VulkanRenderer():
+       current_pipeline(&pipeline_states[0])
+{ }
+
 void VulkanRenderer::begin()
 {
        begin_semaphore = 0;
@@ -30,5 +34,10 @@ void VulkanRenderer::end(Semaphore &sem)
        static_cast<Renderer *>(this)->end();
 }
 
+void VulkanRenderer::set_pipeline_key(uintptr_t key)
+{
+       current_pipeline = &pipeline_states[key];
+}
+
 } // namespace GL
 } // namespace Msp
index fed048410b0e18f26cce9d645fe355723ee54b42..3b0c2f0ff284fc422c27837d66c3d8259418dc37 100644 (file)
@@ -11,15 +11,21 @@ namespace GL {
 class VulkanRenderer: public NonCopyable
 {
 protected:
-       PipelineState pipeline_state;
+       std::map<std::uintptr_t, PipelineState> pipeline_states;
+       PipelineState *current_pipeline = 0;
        Semaphore *begin_semaphore = 0;
        Semaphore *end_semaphore = 0;
 
+       VulkanRenderer();
+
        void begin();
        void begin(Semaphore &);
 
        void end();
        void end(Semaphore &);
+
+       void set_pipeline_key(std::uintptr_t);
+       PipelineState &get_pipeline_state() { return *current_pipeline; }
 };
 
 using RendererBackend = VulkanRenderer;
index 0c5bf77454ed25c32151f2a44ed189723234a192..336b631aaf91c467dbca5577abd81f2b79ebaebe 100644 (file)
@@ -71,9 +71,14 @@ void Renderer::pop_state()
        if(state_stack.size()==1)
                throw stack_underflow("Renderer::pop_state");
 
+       uintptr_t old_pipeline = current_state->pipeline_key;
+
        state_stack.pop_back();
        current_state = &state_stack.back();
        changed |= MATRIX;
+
+       if(current_state->pipeline_key!=old_pipeline)
+               changed |= PIPELINE_KEY;
 }
 
 Renderer::State &Renderer::get_state() const
@@ -85,6 +90,16 @@ Renderer::State &Renderer::get_state() const
        return *current_state;
 }
 
+void Renderer::set_pipeline_key(uintptr_t key)
+{
+       State &state = get_state();
+       if(key!=state.pipeline_key)
+       {
+               state.pipeline_key = key;
+               changed |= PIPELINE_KEY;
+       }
+}
+
 void Renderer::set_camera(const Camera &c)
 {
        get_state().camera = &c;
@@ -242,7 +257,7 @@ void Renderer::set_object_lod_bias(unsigned b)
 void Renderer::clear(const ClearValue *values)
 {
        apply_framebuffer();
-       commands.use_pipeline(&pipeline_state);
+       commands.use_pipeline(&get_pipeline_state());
        commands.clear(values);
 }
 
@@ -250,8 +265,9 @@ void Renderer::draw(const Batch &batch)
 {
        apply_state();
        batch.refresh(frame_index);
-       pipeline_state.set_primitive_type(batch.get_type());
-       commands.use_pipeline(&pipeline_state);
+       PipelineState &ps = get_pipeline_state();
+       ps.set_primitive_type(batch.get_type());
+       commands.use_pipeline(&ps);
        commands.draw(batch);
 }
 
@@ -259,8 +275,9 @@ void Renderer::draw_instanced(const Batch &batch, unsigned count)
 {
        apply_state();
        batch.refresh(frame_index);
-       pipeline_state.set_primitive_type(batch.get_type());
-       commands.use_pipeline(&pipeline_state);
+       PipelineState &ps = get_pipeline_state();
+       ps.set_primitive_type(batch.get_type());
+       commands.use_pipeline(&ps);
        commands.draw_instanced(batch, count);
 }
 
@@ -276,8 +293,8 @@ void Renderer::resolve_multisample(Framebuffer &target)
        if(target.get_width()!=width || target.get_height()!=height)
                throw incompatible_data("Renderer::resolve_multisample");
 
-       pipeline_state.set_framebuffer(state.framebuffer);
-       commands.use_pipeline(&pipeline_state);
+       apply_framebuffer();
+       commands.use_pipeline(&get_pipeline_state());
        commands.resolve_multisample(target);
 }
 
@@ -291,14 +308,27 @@ void Renderer::end_query(const QueryPool &pool, unsigned index)
        commands.end_query(pool, index);
 }
 
+PipelineState &Renderer::get_pipeline_state()
+{
+       if(changed&PIPELINE_KEY)
+       {
+               RendererBackend::set_pipeline_key(current_state->pipeline_key);
+               changed &= ~PIPELINE_KEY;
+       }
+
+       return RendererBackend::get_pipeline_state();
+}
+
 void Renderer::apply_framebuffer()
 {
        const State &state = get_state();
 
-       pipeline_state.set_framebuffer(state.framebuffer);
+       PipelineState &ps = get_pipeline_state();
+
+       ps.set_framebuffer(state.framebuffer);
        static const Rect default_rect = Rect::max();
-       pipeline_state.set_viewport(state.viewport ? *state.viewport : default_rect);
-       pipeline_state.set_scissor(state.scissor ? *state.scissor : default_rect);
+       ps.set_viewport(state.viewport ? *state.viewport : default_rect);
+       ps.set_scissor(state.scissor ? *state.scissor : default_rect);
 }
 
 void Renderer::apply_state()
@@ -310,8 +340,12 @@ void Renderer::apply_state()
 
        apply_framebuffer();
 
-       bool shprog_changed = (state.shprog!=pipeline_state.get_shader_program());
-       pipeline_state.set_shader_program(state.shprog);
+       PipelineState &ps = get_pipeline_state();
+       bool pipeline_changed = (&ps!=last_pipeline);
+       last_pipeline = &ps;
+
+       bool shprog_changed = (state.shprog!=ps.get_shader_program());
+       ps.set_shader_program(state.shprog);
 
        if(changed&MATRIX)
        {
@@ -327,13 +361,13 @@ void Renderer::apply_state()
                shdata_changed = (i->shdata->get_generation()!=i->generation);
        bool extra_shdata = (shdata_stack.size()>state.shdata_count);
 
-       if(shdata_changed || shprog_changed || extra_shdata)
+       if(shdata_changed || shprog_changed || pipeline_changed || extra_shdata)
        {
                if(extra_shdata)
                        shdata_stack.erase(shdata_stack.begin()+state.shdata_count, shdata_stack.end());
                for(const BoundProgramData &d: shdata_stack)
                {
-                       d.shdata->apply(*state.shprog, pipeline_state, frame_index);
+                       d.shdata->apply(*state.shprog, ps, frame_index);
                        d.generation = d.shdata->get_generation();
                }
                changed &= ~SHADER_DATA;
@@ -346,10 +380,10 @@ void Renderer::apply_state()
                if(const VertexArray *array = state.vertex_setup->get_instance_array())
                        array->refresh(frame_index);
        }
-       pipeline_state.set_vertex_setup(state.vertex_setup);
+       ps.set_vertex_setup(state.vertex_setup);
 
-       pipeline_state.set_front_face(state.front_face);
-       pipeline_state.set_face_cull(state.face_cull);
+       ps.set_front_face(state.front_face);
+       ps.set_face_cull(state.face_cull);
 
        if(state.texture_count<texture_stack.size())
                flush_textures();
@@ -360,15 +394,15 @@ void Renderer::apply_state()
                        if(t.binding<0 || shprog_changed)
                                t.binding = state.shprog->get_uniform_binding(t.tag);
                        if(t.binding>=0)
-                               pipeline_state.set_texture(t.binding, t.texture, t.level, t.sampler);
+                               ps.set_texture(t.binding, t.texture, t.level, t.sampler);
                }
 
        static const DepthTest default_depth_test;
-       pipeline_state.set_depth_test(state.depth_test ? *state.depth_test : default_depth_test);
+       ps.set_depth_test(state.depth_test ? *state.depth_test : default_depth_test);
        static const StencilTest default_stencil_test;
-       pipeline_state.set_stencil_test(state.stencil_test ? *state.stencil_test : default_stencil_test);
+       ps.set_stencil_test(state.stencil_test ? *state.stencil_test : default_stencil_test);
        static const Blend default_blend;
-       pipeline_state.set_blend(state.blend ? *state.blend : default_blend);
+       ps.set_blend(state.blend ? *state.blend : default_blend);
 }
 
 
index 51989f7711f8b84bd8a43a1eb8d872cb2a3cf7d2..79bd5cd913931a7eeca01f0e79e358b6965fd329 100644 (file)
@@ -83,6 +83,7 @@ private:
 
        struct State
        {
+               std::uintptr_t pipeline_key = 0;
                const Camera *camera = 0;
                Matrix model_matrix;
                const Framebuffer *framebuffer = 0;
@@ -102,6 +103,7 @@ private:
 
        enum ChangeMask
        {
+               PIPELINE_KEY = 1,
                MATRIX = 2,
                SHADER_DATA = 16
        };
@@ -114,6 +116,7 @@ private:
        std::vector<BoundProgramData> shdata_stack;
        std::vector<BoundTexture> texture_stack;
        const Texture &placeholder_texture;
+       PipelineState *last_pipeline = 0;
        Commands commands;
 
 public:
@@ -140,6 +143,8 @@ private:
        State &get_state() const;
 
 public:
+       void set_pipeline_key(std::uintptr_t);
+
        /** Sets the camera to render from.  The model matrix is reset to identity. */
        void set_camera(const Camera &);
 
@@ -204,6 +209,7 @@ public:
        void end_query(const QueryPool &, unsigned);
 
 private:
+       PipelineState &get_pipeline_state();
        void apply_framebuffer();
        void apply_state();
 };