Commit e4d4dcc3 authored by Florian Oetke's avatar Florian Oetke

fixed crash on resize caused by gui_pass using loaded textures on deferred_renderer recreation

parent 34621d64
Pipeline #3119 passed with stage
in 19 minutes and 10 seconds
......@@ -93,8 +93,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Animation_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -35,6 +35,7 @@ namespace mirrage::renderer {
}
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass> override;
......
......@@ -35,6 +35,7 @@ namespace mirrage::renderer {
auto id() const noexcept -> Render_pass_id override { return render_pass_id_of<Blit_pass_factory>(); }
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass> override;
......
......@@ -55,8 +55,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Bloom_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t> graphics_queue, int current_score)
-> int override;
......
......@@ -32,6 +32,7 @@ namespace mirrage::renderer {
}
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass> override;
......
......@@ -35,8 +35,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Debug_draw_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t> graphics_queue, int current_score)
-> int override;
......
......@@ -57,8 +57,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Deferred_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -53,8 +53,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Depth_of_field_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -30,8 +30,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Frustum_culling_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -43,8 +43,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Gen_mipmap_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -136,8 +136,11 @@ namespace mirrage::renderer {
public:
auto id() const noexcept -> Render_pass_id override { return render_pass_id_of<Gi_pass_factory>(); }
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -18,7 +18,7 @@ namespace mirrage::renderer {
public:
using Factory = Gui_pass_factory;
Gui_pass(Deferred_renderer&, Engine&);
Gui_pass(Deferred_renderer&, Engine&, std::shared_ptr<void> last_state);
void update(util::Time dt) override;
......@@ -31,6 +31,8 @@ namespace mirrage::renderer {
auto name() const noexcept -> const char* override { return "GUI"; }
auto extract_persistent_state() -> std::shared_ptr<void> override;
protected:
void prepare_draw(std::size_t index_count,
std::size_t vertex_count,
......@@ -65,20 +67,34 @@ namespace mirrage::renderer {
bool initialized = false;
};
struct Texture_cache {
Deferred_renderer& _renderer;
vk::UniqueSampler _sampler;
vk::UniqueDescriptorSetLayout _descriptor_set_layout;
std::uintptr_t _next_texture_handle = 0;
std::vector<std::shared_ptr<Loaded_texture>> _loaded_textures;
std::unordered_map<asset::AID, std::weak_ptr<void>> _loaded_textures_by_aid;
std::unordered_map<std::uintptr_t, std::weak_ptr<Loaded_texture>> _loaded_textures_by_handle;
Texture_cache(Deferred_renderer&);
void shrink();
auto load_texture(int width, int height, int channels, const std::uint8_t* data)
-> std::shared_ptr<void>;
auto load_texture(const asset::AID&) -> std::shared_ptr<void>;
};
Deferred_renderer& _renderer;
std::vector<graphic::Framebuffer> _framebuffers;
vk::UniqueSampler _sampler;
vk::UniqueDescriptorSetLayout _descriptor_set_layout;
std::shared_ptr<Texture_cache> _texture_cache;
std::vector<graphic::Framebuffer> _framebuffers;
graphic::Render_pass _render_pass;
graphic::DescriptorSet _descriptor_set;
graphic::Streamed_buffer _mesh_buffer;
// texture cache/store
std::uintptr_t _next_texture_handle = 0;
std::vector<std::shared_ptr<Loaded_texture>> _loaded_textures;
std::unordered_map<asset::AID, std::weak_ptr<void>> _loaded_textures_by_aid;
std::unordered_map<std::uintptr_t, std::weak_ptr<Loaded_texture>> _loaded_textures_by_handle;
// temporary values used during draw
util::maybe<std::uintptr_t> _bound_texture_handle = util::nothing;
util::maybe<vk::CommandBuffer> _current_command_buffer;
......@@ -92,8 +108,11 @@ namespace mirrage::renderer {
auto requires_gbuffer() const noexcept -> bool override { return false; }
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void> last_state,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -96,8 +96,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Particle_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -60,8 +60,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Shadowmapping_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -44,8 +44,11 @@ namespace mirrage::renderer {
public:
auto id() const noexcept -> Render_pass_id override { return render_pass_id_of<Ssao_pass_factory>(); }
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -53,8 +53,11 @@ namespace mirrage::renderer {
public:
auto id() const noexcept -> Render_pass_id override { return render_pass_id_of<Taa_pass_factory>(); }
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -72,8 +72,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Tone_mapping_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -50,8 +50,11 @@ namespace mirrage::renderer {
return render_pass_id_of<Transparent_pass_factory>();
}
auto create_pass(Deferred_renderer&, util::maybe<ecs::Entity_manager&>, Engine&, bool&)
-> std::unique_ptr<Render_pass> override;
auto create_pass(Deferred_renderer&,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass> override;
auto rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t>, int) -> int override;
......
......@@ -172,6 +172,10 @@ namespace mirrage::renderer {
virtual void process_camera(Camera_state&) {} //< allows passes to modify the current camera
virtual auto name() const noexcept -> const char* = 0;
/// API to allow render passes to save some of their state (e.g. loaded textures/data)
/// across pipeline/renderer recreation
virtual auto extract_persistent_state() -> std::shared_ptr<void> { return {}; }
};
using Render_pass_id = util::type_uid_t;
......@@ -184,6 +188,7 @@ namespace mirrage::renderer {
virtual auto id() const noexcept -> Render_pass_id = 0;
virtual auto create_pass(Deferred_renderer&,
std::shared_ptr<void> last_state,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass> = 0;
......
......@@ -101,7 +101,8 @@ namespace mirrage::renderer {
, _pass_factories(std::move(passes))
, _passes(util::map(_pass_factories,
[&, write_first_pp_buffer = true](auto& factory) mutable {
return factory->create_pass(*this, ecs, engine, write_first_pp_buffer);
return factory->create_pass(
*this, std::shared_ptr<void>{}, ecs, engine, write_first_pp_buffer);
}))
, _cameras(ecs.is_some() ? util::justPtr(&ecs.get_or_throw().list<Camera_comp>()) : util::nothing)
{
......@@ -130,9 +131,15 @@ namespace mirrage::renderer {
LOG(plog::warning) << "--recreate";
device().wait_idle();
auto persisted_pass_states = std::vector<std::shared_ptr<void>>();
for(auto& pass : _passes) {
if(pass)
if(pass) {
persisted_pass_states.emplace_back(pass->extract_persistent_state());
pass.reset();
} else {
persisted_pass_states.emplace_back();
}
}
if(gbuffer_required(_pass_factories)) {
......@@ -146,10 +153,10 @@ namespace mirrage::renderer {
auto write_first_pp_buffer = true;
for(auto i = std::size_t(0); i < _passes.size(); i++) {
_passes[i] = _pass_factories.at(i)->create_pass(
*this, _entity_manager, *_engine, write_first_pp_buffer);
*this, persisted_pass_states.at(i), _entity_manager, *_engine, write_first_pp_buffer);
}
_profiler = graphic::Profiler(device(), 64);
_profiler = graphic::Profiler(device(), 128);
device().wait_idle();
}
......
......@@ -289,7 +289,8 @@ namespace mirrage::renderer {
}
auto Animation_pass_factory::create_pass(Deferred_renderer& renderer,
auto Animation_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&> entities,
Engine&,
bool&) -> std::unique_ptr<Render_pass>
......
......@@ -103,7 +103,8 @@ namespace mirrage::renderer {
}
auto Billboard_pass_factory::create_pass(Deferred_renderer& renderer,
auto Billboard_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&> entities,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -91,6 +91,7 @@ namespace mirrage::renderer {
auto Blit_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -265,6 +265,7 @@ namespace mirrage::renderer {
auto Bloom_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -24,6 +24,7 @@ namespace mirrage::renderer {
auto Clear_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass>
......
......@@ -123,6 +123,7 @@ namespace mirrage::renderer {
auto Debug_draw_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -435,7 +435,8 @@ namespace mirrage::renderer {
}
}
auto Deferred_pass_factory::create_pass(Deferred_renderer& renderer,
auto Deferred_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&> entities,
Engine&,
bool& use_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -168,6 +168,7 @@ namespace mirrage::renderer {
auto Depth_of_field_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -227,7 +227,8 @@ namespace mirrage::renderer {
}
auto Frustum_culling_pass_factory::create_pass(Deferred_renderer& renderer,
auto Frustum_culling_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&> entities,
Engine&,
bool&) -> std::unique_ptr<Render_pass>
......
......@@ -185,6 +185,7 @@ namespace mirrage::renderer {
auto Gen_mipmap_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool&) -> std::unique_ptr<Render_pass>
......
......@@ -1321,6 +1321,7 @@ namespace mirrage::renderer {
auto Gi_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void>,
util::maybe<ecs::Entity_manager&>,
Engine&,
bool& write_first_pp_buffer) -> std::unique_ptr<Render_pass>
......
......@@ -77,7 +77,7 @@ namespace mirrage::renderer {
} // namespace
Gui_pass::Gui_pass(Deferred_renderer& drenderer, Engine&)
Gui_pass::Gui_pass(Deferred_renderer& drenderer, Engine&, std::shared_ptr<void> last_state)
: _renderer(drenderer)
, _sampler(drenderer.device().create_sampler(1,
vk::SamplerAddressMode::eClampToEdge,
......@@ -85,14 +85,41 @@ namespace mirrage::renderer {
vk::Filter::eLinear,
vk::SamplerMipmapMode::eNearest))
, _descriptor_set_layout(create_descriptor_set_layout(drenderer.device(), *_sampler))
, _texture_cache(last_state ? std::static_pointer_cast<Texture_cache>(last_state)
: std::make_unique<Texture_cache>(drenderer))
, _render_pass(build_render_pass(drenderer, *_descriptor_set_layout, _framebuffers))
, _descriptor_set(drenderer.create_descriptor_set(*_descriptor_set_layout, 1))
, _mesh_buffer(drenderer.device(),
max_render_buffer_size,
vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eVertexBuffer)
{
_texture_cache->shrink();
}
Gui_pass::Texture_cache::Texture_cache(Deferred_renderer& r)
: _renderer(r)
, _sampler(r.device().create_sampler(1,
vk::SamplerAddressMode::eClampToEdge,
vk::BorderColor::eIntOpaqueBlack,
vk::Filter::eLinear,
vk::SamplerMipmapMode::eNearest))
, _descriptor_set_layout(create_descriptor_set_layout(r.device(), *_sampler))
{
}
auto Gui_pass::extract_persistent_state() -> std::shared_ptr<void> { return _texture_cache; }
void Gui_pass::Texture_cache::shrink()
{
for(auto& texture : _loaded_textures) {
if(texture.use_count() == 1) {
texture.reset();
}
}
util::erase_if(_loaded_textures, [](auto& t) { return !t; });
util::erase_if(_loaded_textures_by_aid, [](auto& t) { return t.second.expired(); });
util::erase_if(_loaded_textures_by_handle, [](auto& t) { return t.second.expired(); });
}
void Gui_pass::update(util::Time) {}
......@@ -109,14 +136,7 @@ namespace mirrage::renderer {
_current_command_buffer = util::nothing;
// remove unused textures from cache
for(auto& texture : _loaded_textures) {
if(texture.use_count() == 1) {
texture.reset();
}
}
util::erase_if(_loaded_textures, [](auto& t) { return !t; });
util::erase_if(_loaded_textures_by_aid, [](auto& t) { return t.second.expired(); });
util::erase_if(_loaded_textures_by_handle, [](auto& t) { return t.second.expired(); });
_texture_cache->shrink();
}
Gui_pass::Loaded_texture::Loaded_texture(std::uintptr_t handle,
......@@ -157,6 +177,11 @@ namespace mirrage::renderer {
auto Gui_pass::load_texture(int width, int height, int channels, const std::uint8_t* data)
-> std::shared_ptr<void>
{
return _texture_cache->load_texture(width, height, channels, data);
}
auto Gui_pass::Texture_cache::load_texture(int width, int height, int channels, const std::uint8_t* data)
-> std::shared_ptr<void>
{
auto handle = _next_texture_handle++;
......@@ -186,6 +211,10 @@ namespace mirrage::renderer {
}
auto Gui_pass::load_texture(const asset::AID& aid) -> std::shared_ptr<void>
{
return _texture_cache->load_texture(aid);
}
auto Gui_pass::Texture_cache::load_texture(const asset::AID& aid) -> std::shared_ptr<void>
{
auto cache_entry = _loaded_textures_by_aid[aid];
if(auto sp = cache_entry.lock()) {
......@@ -259,7 +288,7 @@ namespace mirrage::renderer {
"Gui_pass::prepare_draw has to be called inside a draw call!");
if(_bound_texture_handle.is_nothing() || int_tex_handle != _bound_texture_handle.get_or_throw()) {
auto texture = _loaded_textures_by_handle[int_tex_handle].lock();
auto texture = _texture_cache->_loaded_textures_by_handle[int_tex_handle].lock();
MIRRAGE_INVARIANT(texture,
"The requested texture (" << int_tex_handle
<< ") has not been loaded or already been freed!");
......@@ -284,12 +313,14 @@ namespace mirrage::renderer {
void Gui_pass::finalize_draw() { _render_pass.unsafe_end_renderpass(); }
auto Gui_pass_factory::create_pass(Deferred_renderer& renderer,
auto Gui_pass_factory::create_pass(Deferred_renderer& renderer,
std::shared_ptr<void> last_state,
util::maybe<ecs::Entity_manager&>,
Engine& engine,