From ab9b22356f16aea822527c06186641d3121e1355 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 17 Jan 2022 22:41:24 +0200 Subject: [PATCH] Support multiple PipelineStates in Renderer 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 | 3 + source/backends/vulkan/renderer_backend.cpp | 9 +++ source/backends/vulkan/renderer_backend.h | 8 ++- source/render/renderer.cpp | 76 +++++++++++++++------ source/render/renderer.h | 6 ++ 5 files changed, 80 insertions(+), 22 deletions(-) diff --git a/source/backends/opengl/renderer_backend.h b/source/backends/opengl/renderer_backend.h index 4adcce3a..685b6464 100644 --- a/source/backends/opengl/renderer_backend.h +++ b/source/backends/opengl/renderer_backend.h @@ -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; diff --git a/source/backends/vulkan/renderer_backend.cpp b/source/backends/vulkan/renderer_backend.cpp index ab77158a..3b9281b5 100644 --- a/source/backends/vulkan/renderer_backend.cpp +++ b/source/backends/vulkan/renderer_backend.cpp @@ -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(this)->end(); } +void VulkanRenderer::set_pipeline_key(uintptr_t key) +{ + current_pipeline = &pipeline_states[key]; +} + } // namespace GL } // namespace Msp diff --git a/source/backends/vulkan/renderer_backend.h b/source/backends/vulkan/renderer_backend.h index fed04841..3b0c2f0f 100644 --- a/source/backends/vulkan/renderer_backend.h +++ b/source/backends/vulkan/renderer_backend.h @@ -11,15 +11,21 @@ namespace GL { class VulkanRenderer: public NonCopyable { protected: - PipelineState pipeline_state; + std::map 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; diff --git a/source/render/renderer.cpp b/source/render/renderer.cpp index 0c5bf774..336b631a 100644 --- a/source/render/renderer.cpp +++ b/source/render/renderer.cpp @@ -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_countget_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); } diff --git a/source/render/renderer.h b/source/render/renderer.h index 51989f77..79bd5cd9 100644 --- a/source/render/renderer.h +++ b/source/render/renderer.h @@ -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 shdata_stack; std::vector 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(); }; -- 2.43.0