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.
void begin() { }
void end();
void begin() { }
void end();
+
+ void set_pipeline_key(std::uintptr_t) { }
+ PipelineState &get_pipeline_state() { return pipeline_state; }
};
using RendererBackend = OpenGLRenderer;
};
using RendererBackend = OpenGLRenderer;
namespace Msp {
namespace GL {
namespace Msp {
namespace GL {
+VulkanRenderer::VulkanRenderer():
+ current_pipeline(&pipeline_states[0])
+{ }
+
void VulkanRenderer::begin()
{
begin_semaphore = 0;
void VulkanRenderer::begin()
{
begin_semaphore = 0;
static_cast<Renderer *>(this)->end();
}
static_cast<Renderer *>(this)->end();
}
+void VulkanRenderer::set_pipeline_key(uintptr_t key)
+{
+ current_pipeline = &pipeline_states[key];
+}
+
} // namespace GL
} // namespace Msp
} // namespace GL
} // namespace Msp
class VulkanRenderer: public NonCopyable
{
protected:
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;
Semaphore *begin_semaphore = 0;
Semaphore *end_semaphore = 0;
void begin();
void begin(Semaphore &);
void end();
void end(Semaphore &);
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;
};
using RendererBackend = VulkanRenderer;
if(state_stack.size()==1)
throw stack_underflow("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;
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
}
Renderer::State &Renderer::get_state() const
+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;
void Renderer::set_camera(const Camera &c)
{
get_state().camera = &c;
void Renderer::clear(const ClearValue *values)
{
apply_framebuffer();
void Renderer::clear(const ClearValue *values)
{
apply_framebuffer();
- commands.use_pipeline(&pipeline_state);
+ commands.use_pipeline(&get_pipeline_state());
commands.clear(values);
}
commands.clear(values);
}
{
apply_state();
batch.refresh(frame_index);
{
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);
{
apply_state();
batch.refresh(frame_index);
{
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);
}
commands.draw_instanced(batch, count);
}
if(target.get_width()!=width || target.get_height()!=height)
throw incompatible_data("Renderer::resolve_multisample");
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);
}
commands.resolve_multisample(target);
}
commands.end_query(pool, 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();
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();
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()
}
void Renderer::apply_state()
- 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);
shdata_changed = (i->shdata->get_generation()!=i->generation);
bool extra_shdata = (shdata_stack.size()>state.shdata_count);
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)
{
{
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;
d.generation = d.shdata->get_generation();
}
changed &= ~SHADER_DATA;
if(const VertexArray *array = state.vertex_setup->get_instance_array())
array->refresh(frame_index);
}
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();
if(state.texture_count<texture_stack.size())
flush_textures();
if(t.binding<0 || shprog_changed)
t.binding = state.shprog->get_uniform_binding(t.tag);
if(t.binding>=0)
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;
}
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;
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;
static const Blend default_blend;
- pipeline_state.set_blend(state.blend ? *state.blend : default_blend);
+ ps.set_blend(state.blend ? *state.blend : default_blend);
+ std::uintptr_t pipeline_key = 0;
const Camera *camera = 0;
Matrix model_matrix;
const Framebuffer *framebuffer = 0;
const Camera *camera = 0;
Matrix model_matrix;
const Framebuffer *framebuffer = 0;
MATRIX = 2,
SHADER_DATA = 16
};
MATRIX = 2,
SHADER_DATA = 16
};
std::vector<BoundProgramData> shdata_stack;
std::vector<BoundTexture> texture_stack;
const Texture &placeholder_texture;
std::vector<BoundProgramData> shdata_stack;
std::vector<BoundTexture> texture_stack;
const Texture &placeholder_texture;
+ PipelineState *last_pipeline = 0;
Commands commands;
public:
Commands commands;
public:
State &get_state() const;
public:
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 &);
/** Sets the camera to render from. The model matrix is reset to identity. */
void set_camera(const Camera &);
void end_query(const QueryPool &, unsigned);
private:
void end_query(const QueryPool &, unsigned);
private:
+ PipelineState &get_pipeline_state();
void apply_framebuffer();
void apply_state();
};
void apply_framebuffer();
void apply_state();
};