Commit a08828a0 authored by Florian Oetke's avatar Florian Oetke
Browse files

reloadable renderer

parent 18b68c81
......@@ -62,8 +62,7 @@ namespace mirrage {
, _gui(engine.window().viewport(),
engine.assets(),
engine.input(),
_meta_system.renderer().find_pass<gui::Gui_renderer>().get_or_throw(
"No renderer specified to render UI elements!"))
util::tracking_ptr<gui::Gui_renderer>(_meta_system.renderer().find_pass<renderer::Gui_pass>()))
, _performance_log(util::nothing) {
_camera = _meta_system.entities().emplace("camera");
......@@ -231,9 +230,11 @@ namespace mirrage {
_sun.get<renderer::Directional_light_comp>().process(
[&](renderer::Directional_light_comp& light) { light.temperature(_sun_color_temperature); });
auto s = _meta_system.renderer().settings();
auto s = _meta_system.renderer().settings();
//if(s.debug_disect != p.disect_model) {
s.debug_disect = p.disect_model;
_meta_system.renderer().settings(s);
//}
_update_sun_position();
}
......@@ -362,7 +363,7 @@ namespace mirrage {
nk_label(ctx, "Indirect illumination", NK_TEXT_LEFT);
int gi_active = renderer_settings.gi ? 1 : 0;
if(nk_checkbox_label(ctx, "GI", &gi_active)) {
if(nk_checkbox_label(ctx, "GI", &gi_active) && renderer_settings.gi != (gi_active == 1)) {
auto rs_copy = renderer_settings;
rs_copy.gi = gi_active == 1;
_meta_system.renderer().settings(rs_copy);
......
......@@ -25,6 +25,12 @@ namespace mirrage::graphic {
Base_texture(Base_texture&& rhs) noexcept
: _image(std::move(rhs._image)), _image_view(std::move(rhs._image_view)) {}
Base_texture& operator=(Base_texture&& rhs) noexcept {
_image = std::move(rhs._image);
_image_view = std::move(rhs._image_view);
return *this;
}
auto view() const noexcept { return *_image_view; }
auto image() const noexcept { return _image.image(); }
......@@ -120,6 +126,8 @@ namespace mirrage::graphic {
Render_target(Render_target&& rhs) noexcept
: Texture<Type>(std::move(rhs)), _single_mip_level_views(std::move(rhs._single_mip_level_views)) {}
Render_target& operator=(Render_target&&) = default;
using Texture<Type>::width;
using Texture<Type>::height;
using Texture<Type>::view;
......@@ -158,7 +166,7 @@ namespace mirrage::graphic {
void shrink_to_fit();
private:
Device& _device;
Device* _device;
std::uint32_t _owner_qfamily;
std::unordered_map<asset::AID, Texture_ptr> _textures;
};
......
......@@ -43,6 +43,7 @@ namespace mirrage::graphic {
, _generate_mips(generate_mips)
, _dimensions(dimensions) {}
Static_image(Static_image&&) noexcept;
Static_image& operator=(Static_image&&) noexcept;
auto image() const noexcept { return *_image; }
......@@ -100,7 +101,7 @@ namespace mirrage::graphic {
private:
Backed_buffer _buffer;
const std::size_t _capacity;
std::size_t _capacity;
vk::PipelineStageFlags _earliest_usage;
vk::AccessFlags _earliest_usage_access;
vk::PipelineStageFlags _latest_usage;
......
......@@ -57,10 +57,10 @@ namespace mirrage::graphic {
Image_dimensions(std::uint32_t width, std::uint32_t height, std::uint32_t depth, std::uint32_t layers)
: width(width), height(height), depth(depth), layers(layers) {}
const std::uint32_t width;
const std::uint32_t height;
const std::uint32_t depth;
const std::uint32_t layers;
std::uint32_t width;
std::uint32_t height;
std::uint32_t depth;
std::uint32_t layers;
};
template <Image_type Type>
struct Image_dimensions_t;
......@@ -106,9 +106,12 @@ namespace mirrage::graphic {
class Descriptor_pool {
public:
Descriptor_pool(Descriptor_pool&&) = default;
Descriptor_pool& operator=(Descriptor_pool&&) = default;
~Descriptor_pool() = default;
Descriptor_pool(Descriptor_pool&& rhs) : _device(rhs._device), _pool(std::move(rhs._pool)) {}
Descriptor_pool& operator=(Descriptor_pool&& rhs) {
_pool = std::move(rhs._pool);
return *this;
}
~Descriptor_pool() = default;
auto create_descriptor(vk::DescriptorSetLayout) -> vk::UniqueDescriptorSet;
......
......@@ -133,19 +133,19 @@ namespace mirrage::graphic {
Texture_cache::Texture_cache(Device& device, std::uint32_t owner_qfamily)
: _device(device), _owner_qfamily(owner_qfamily) {}
: _device(&device), _owner_qfamily(owner_qfamily) {}
auto Texture_cache::load(const asset::AID& id) -> Texture_ptr {
auto& cached = _textures[id];
if(!cached) {
auto in = _device.context().asset_manager().load_raw(id);
auto in = _device->context().asset_manager().load_raw(id);
auto header =
parse_header(in.get_or_throw("Texture '", id.str(), "' couldn't be opened!"), id.str());
auto dimensions = Image_dimensions(header.width, header.height, header.depth, header.layers);
auto image = _device.transfer().upload_image(
auto image = _device->transfer().upload_image(
vk_type(header.type),
_owner_qfamily,
dimensions,
......@@ -155,7 +155,7 @@ namespace mirrage::graphic {
[&](char* dest) { in.get_or_throw().read_direct(dest, header.size); });
// TODO: create different type based on header.type
cached = std::make_shared<Texture_2D>(_device, std::move(image), header.format);
cached = std::make_shared<Texture_2D>(*_device, std::move(image), header.format);
}
return cached;
......
......@@ -16,6 +16,14 @@ namespace mirrage::graphic {
, _mip_count(std::move(rhs._mip_count))
, _dimensions(std::move(rhs._dimensions)) {}
Static_image& Static_image::operator=(Static_image&& rhs) noexcept {
_image = std::move(rhs._image);
_mip_count = std::move(rhs._mip_count);
_dimensions = std::move(rhs._dimensions);
return *this;
}
void Dynamic_buffer::update(const Command_buffer& cb, vk::DeviceSize offset, gsl::span<const char> data) {
INVARIANT(_capacity >= gsl::narrow<std::size_t>(data.size() + offset), "Buffer overflow");
INVARIANT(data.size() % 4 == 0, "buffer size has to be a multiple of 4: " << data.size());
......@@ -91,7 +99,7 @@ namespace mirrage::graphic {
, _semaphore(device.create_semaphore())
, _command_buffer_pool(device.create_command_buffer_pool(transfer_queue, true, true))
, _command_buffers(device,
+[](vk::UniqueCommandBuffer& cb) { cb.reset({}); },
+[](vk::UniqueCommandBuffer& cb) { cb->reset({}); },
[&] { return std::move(_command_buffer_pool.create_primary()[0]); },
max_frames) {
......
......@@ -16,6 +16,7 @@
#include <nuklear.h>
#include <mirrage/utils/maybe.hpp>
#include <mirrage/utils/template_utils.hpp>
#include <mirrage/utils/units.hpp>
#include <glm/vec2.hpp>
......@@ -87,10 +88,10 @@ namespace mirrage::gui {
// https://github.com/vurtun/nuklear/blob/master/example/skinning.c
class Gui {
public:
Gui(glm::vec4 viewport,
asset::Asset_manager& assets,
input::Input_manager& input,
Gui_renderer& renderer);
Gui(glm::vec4 viewport,
asset::Asset_manager& assets,
input::Input_manager& input,
util::tracking_ptr<Gui_renderer> renderer);
~Gui();
void draw();
......@@ -106,8 +107,14 @@ namespace mirrage::gui {
private:
struct PImpl;
Gui_renderer& _renderer;
std::unique_ptr<PImpl> _impl;
glm::vec4 _viewport;
asset::Asset_manager& _assets;
input::Input_manager& _input;
util::tracking_ptr<Gui_renderer> _renderer;
Gui_renderer* _last_renderer;
std::unique_ptr<PImpl> _impl;
void _init();
};
......
......@@ -411,15 +411,34 @@ namespace mirrage::gui {
~PImpl() {}
};
Gui::Gui(glm::vec4 viewport,
asset::Asset_manager& assets,
input::Input_manager& input,
Gui_renderer& renderer)
: _renderer(renderer), _impl(std::make_unique<PImpl>(viewport, assets, input, renderer)) {
Gui::Gui(glm::vec4 viewport,
asset::Asset_manager& assets,
input::Input_manager& input,
util::tracking_ptr<Gui_renderer> renderer)
: _viewport(viewport)
, _assets(assets)
, _input(input)
, _renderer(renderer)
, _last_renderer(_renderer.get()) {
_init();
}
Gui::~Gui() {
if(_renderer) {
_renderer->_gui = nullptr;
}
}
void Gui::_init() {
_last_renderer = _renderer.get();
if(_last_renderer) {
_impl = std::make_unique<PImpl>(_viewport, _assets, _input, *_last_renderer);
_last_renderer->_gui = this;
_renderer._gui = this;
} else {
ERROR("Gui initialized without a valid renderer. Nothing will be drawn!");
}
}
Gui::~Gui() { _renderer._gui = nullptr; }
void Gui_renderer::draw_gui() {
if(_gui) {
......@@ -431,7 +450,13 @@ namespace mirrage::gui {
_impl->renderer.draw(_impl->ctx.ctx, _impl->viewport, _impl->screen_size, _impl->ui_matrix);
}
auto Gui::ctx() -> nk_context* { return &_impl->ctx.ctx; }
auto Gui::ctx() -> nk_context* {
if(_renderer && _renderer.get() != _last_renderer) {
_init();
}
return &_impl->ctx.ctx;
}
auto Gui::centered(int width, int height) -> struct nk_rect {
return nk_rect(_impl->screen_size.x / 2.f - width / 2.f,
......
......@@ -27,26 +27,28 @@ namespace mirrage::renderer {
struct Renderer_settings {
int shadowmap_resolution = 4096;
int shadow_quality = 99; // 0 = lowest
int gi_diffuse_mip_level = 1; // >=1 !
int gi_specular_mip_level = 0;
int shadowmap_resolution = 4096;
int shadow_quality = 99; // 0 = lowest
bool gi = true;
bool gi_highres = true;
int gi_diffuse_mip_level = 1;
int gi_specular_mip_level = 0;
int gi_samples = 128;
bool gi_prioritise_near_samples = true;
bool gi_low_quality_mip_levels = 0;
float exposure_override = -1.f;
bool ssao = true;
bool gi = true;
bool gi_highres = true;
bool dynamic_shadows = false;
bool debug_disect = false;
int debug_gi_layer = -1;
};
#ifdef sf2_structDef
sf2_structDef(Renderer_settings,
shadowmap_resolution,
shadow_quality,
gi,
dynamic_shadows,
debug_disect,
debug_gi_layer);
sf2_structDef(
Renderer_settings, shadowmap_resolution, shadow_quality, gi, dynamic_shadows, debug_gi_layer);
#endif
struct Global_uniforms {
......@@ -121,25 +123,28 @@ namespace mirrage::renderer {
void finish_frame();
auto settings() const -> auto& { return *_settings; }
void settings(const Renderer_settings& s);
void settings(const Renderer_settings& s, bool apply = true);
void save_settings();
private:
friend class Deferred_renderer;
using Pass_factories = std::vector<std::unique_ptr<Pass_factory>>;
using Settings_ptr = std::shared_ptr<const Renderer_settings>;
Settings_ptr _settings;
Pass_factories _pass_factories;
graphic::Window& _window;
graphic::Device_ptr _device;
const graphic::Swapchain& _swapchain;
std::uint32_t _queue_family;
vk::Queue _queue;
vk::UniqueSemaphore _image_acquired;
vk::UniqueSemaphore _image_presented;
graphic::Command_buffer_pool _command_buffer_pool;
util::maybe<std::size_t> _aquired_swapchain_image;
std::vector<vk::CommandBuffer> _queued_commands;
Settings_ptr _settings;
Pass_factories _pass_factories;
graphic::Window& _window;
graphic::Device_ptr _device;
const graphic::Swapchain& _swapchain;
std::uint32_t _queue_family;
vk::Queue _queue;
vk::UniqueSemaphore _image_acquired;
vk::UniqueSemaphore _image_presented;
graphic::Command_buffer_pool _command_buffer_pool;
util::maybe<std::size_t> _aquired_swapchain_image;
std::vector<vk::CommandBuffer> _queued_commands;
std::vector<Deferred_renderer*> _renderer_instances;
bool _recreation_pending = false;
auto _rank_device(vk::PhysicalDevice, util::maybe<std::uint32_t> gqueue) -> int;
auto _init_device(vk::PhysicalDevice, util::maybe<std::uint32_t> gqueue)
......@@ -148,21 +153,25 @@ namespace mirrage::renderer {
auto _aquire_next_image() -> std::size_t;
};
class Deferred_renderer {
public:
Deferred_renderer(Deferred_renderer_factory&,
std::vector<std::unique_ptr<Pass_factory>>&,
ecs::Entity_manager&,
util::maybe<Meta_system&>);
Deferred_renderer(const Deferred_renderer&) = delete;
auto operator=(const Deferred_renderer&) -> Deferred_renderer& = delete;
~Deferred_renderer();
void recreate();
template <class T>
auto find_pass() -> util::maybe<T&> {
auto find_pass() -> util::tracking_ptr<T> {
auto pass = std::find_if(
_passes.begin(), _passes.end(), [](auto& p) { return dynamic_cast<T*>(&*p) != nullptr; });
return pass != _passes.end() ? util::justPtr(dynamic_cast<T*>(&**pass)) : util::nothing;
return pass != _passes.end() ? util::tracking_ptr<T>(pass->create_ptr())
: util::tracking_ptr<T>{};
}
void update(util::Time dt);
......@@ -170,53 +179,59 @@ namespace mirrage::renderer {
void shrink_to_fit();
auto texture_cache() -> auto& { return _texture_cache; }
auto model_loader() -> auto& { return _model_loader; }
auto texture_cache() -> auto& { return *_texture_cache; }
auto model_loader() -> auto& { return *_model_loader; }
auto gbuffer() noexcept -> auto& { return _gbuffer; }
auto gbuffer() const noexcept -> auto& { return _gbuffer; }
auto gbuffer() noexcept -> auto& { return *_gbuffer; }
auto gbuffer() const noexcept -> auto& { return *_gbuffer; }
auto global_uniforms() const noexcept -> auto& { return _global_uniforms; }
auto global_uniforms_layout() const noexcept { return *_global_uniform_descriptor_set_layout; }
auto device() noexcept -> auto& { return *_factory._device; }
auto window() noexcept -> auto& { return _factory._window; }
auto swapchain() noexcept -> auto& { return _factory._swapchain; }
auto queue_family() const noexcept { return _factory._queue_family; }
auto device() noexcept -> auto& { return *_factory->_device; }
auto window() noexcept -> auto& { return _factory->_window; }
auto swapchain() noexcept -> auto& { return _factory->_swapchain; }
auto queue_family() const noexcept { return _factory->_queue_family; }
auto create_descriptor_set(vk::DescriptorSetLayout) -> vk::UniqueDescriptorSet;
auto descriptor_pool() noexcept -> auto& { return _descriptor_set_pool; }
auto active_camera() noexcept -> util::maybe<Camera_state&>;
auto settings() const -> auto& { return _factory.settings(); }
void settings(const Renderer_settings& s) { _factory.settings(s); }
auto settings() const -> auto& { return _factory->settings(); }
void save_settings() { _factory->save_settings(); }
void settings(const Renderer_settings& s, bool apply = true) { _factory->settings(s, apply); }
auto profiler() const noexcept -> auto& { return _profiler; }
auto profiler() noexcept -> auto& { return _profiler; }
private:
Deferred_renderer_factory& _factory;
Deferred_renderer_factory* _factory;
ecs::Entity_manager* _entity_manager;
util::maybe<Meta_system&> _meta_system;
graphic::Descriptor_pool _descriptor_set_pool;
GBuffer _gbuffer;
Global_uniforms _global_uniforms;
graphic::Profiler _profiler;
float _time_acc = 0.f;
float _delta_time = 0.f;
std::unique_ptr<GBuffer> _gbuffer;
Global_uniforms _global_uniforms;
graphic::Profiler _profiler;
float _time_acc = 0.f;
float _delta_time = 0.f;
graphic::Texture_cache _texture_cache;
Model_loader _model_loader;
std::unique_ptr<graphic::Texture_cache> _texture_cache;
std::unique_ptr<Model_loader> _model_loader;
vk::UniqueDescriptorSetLayout _global_uniform_descriptor_set_layout;
vk::UniqueDescriptorSet _global_uniform_descriptor_set;
graphic::Dynamic_buffer _global_uniform_buffer;
std::vector<std::unique_ptr<Pass>> _passes;
std::vector<util::trackable<Pass>> _passes;
Camera_comp::Pool& _cameras;
Camera_comp::Pool* _cameras;
util::maybe<Camera_state> _active_camera;
void _write_global_uniform_descriptor_set();
void _update_global_uniforms(vk::CommandBuffer, const Camera_state& camera);
auto operator=(Deferred_renderer &&) -> Deferred_renderer& = default;
};
}
......@@ -9,7 +9,7 @@ namespace mirrage::renderer {
public:
GBuffer(graphic::Device& device, std::uint32_t width, std::uint32_t height);
const std::uint32_t mip_levels;
std::uint32_t mip_levels;
vk::Format depth_format;
graphic::Render_target_2D depth; // depth-buffer
......
......@@ -181,6 +181,8 @@ namespace mirrage::renderer {
std::uint32_t owner_qfamily,
graphic::Texture_cache&,
std::size_t max_unique_materials);
Model_loader(Model_loader&&) = default;
Model_loader& operator=(Model_loader&&) = default;
~Model_loader();
auto load(const asset::AID&) -> future<Model_ptr>;
......@@ -191,9 +193,9 @@ namespace mirrage::renderer {
private:
graphic::Device& _device;
graphic::Device* _device;
std::uint32_t _owner_qfamily;
graphic::Texture_cache& _texture_cache;
graphic::Texture_cache* _texture_cache;
vk::UniqueSampler _sampler;
vk::UniqueDescriptorSetLayout _material_descriptor_set_layout;
......
......@@ -62,7 +62,6 @@ namespace mirrage::renderer {
graphic::Streamed_buffer _mesh_buffer;
// texture cache/store
graphic::Texture_cache _texture_cache;
int _next_texture_handle = 0;
std::vector<std::shared_ptr<Loaded_texture>> _loaded_textures;
std::unordered_map<asset::AID, std::weak_ptr<struct nk_image>> _loaded_textures_by_aid;
......
......@@ -16,7 +16,9 @@ namespace mirrage::renderer {
std::vector<std::unique_ptr<Pass_factory>>& passes,
ecs::Entity_manager& ecs,
util::maybe<Meta_system&> userdata)
: _factory(factory)
: _factory(&factory)
, _entity_manager(&ecs)
, _meta_system(userdata)
, _descriptor_set_pool(
device().create_descriptor_pool(256,
{{vk::DescriptorType::eUniformBuffer, 8},
......@@ -24,11 +26,13 @@ namespace mirrage::renderer {
{vk::DescriptorType::eInputAttachment, 64},
{vk::DescriptorType::eSampledImage, 256},
{vk::DescriptorType::eSampler, 64}}))
, _gbuffer(device(), factory._window.width(), factory._window.height())
, _gbuffer(std::make_unique<GBuffer>(device(), factory._window.width(), factory._window.height()))
, _profiler(device(), 64)
, _texture_cache(device(), device().get_queue_family("draw"_strid))
, _model_loader(device(), device().get_queue_family("draw"_strid), _texture_cache, 64)
, _texture_cache(
std::make_unique<graphic::Texture_cache>(device(), device().get_queue_family("draw"_strid)))
, _model_loader(std::make_unique<Model_loader>(
device(), device().get_queue_family("draw"_strid), *_texture_cache, 64))
, _global_uniform_descriptor_set_layout(
device().create_descriptor_set_layout(vk::DescriptorSetLayoutBinding{
......@@ -46,18 +50,44 @@ namespace mirrage::renderer {
vk::PipelineStageFlagBits::eFragmentShader,
vk::AccessFlagBits::eUniformRead))
, _passes(util::map(passes, [&, write_first_pp_buffer = true](auto& factory) mutable {
return factory->create_pass(*this, ecs, userdata, write_first_pp_buffer);
return util::trackable<Pass>(factory->create_pass(*this, ecs, userdata, write_first_pp_buffer));
}))
, _cameras(ecs.list<Camera_comp>()) {
, _cameras(&ecs.list<Camera_comp>()) {
_write_global_uniform_descriptor_set();
factory._renderer_instances.emplace_back(this);
}
Deferred_renderer::~Deferred_renderer() {
util::erase_fast(_factory->_renderer_instances, this);
device().print_memory_usage(std::cout);
device().wait_idle();
_passes.clear();
}
void Deferred_renderer::recreate() {
device().wait_idle();
for(auto& pass : _passes) {
pass.reset();
}
_gbuffer.reset();
// recreate gbuffer and renderpasses
_gbuffer = std::make_unique<GBuffer>(device(), _factory->_window.width(), _factory->_window.height());
auto write_first_pp_buffer = true;
for(auto i = std::size_t(0); i < _passes.size(); i++) {
_passes[i] = _factory->_pass_factories.at(i)->create_pass(
*this, *_entity_manager, _meta_system, write_first_pp_buffer);
}
_write_global_uniform_descriptor_set();
device().wait_idle();
}
void Deferred_renderer::_write_global_uniform_descriptor_set() {
auto buffer_info =
vk::DescriptorBufferInfo(_global_uniform_buffer.buffer(), 0, sizeof(Global_uniforms));
......@@ -86,7 +116,7 @@ namespace mirrage::renderer {
if(active_camera().is_nothing())
return;
auto main_command_buffer = _factory.queue_temporary_command_buffer();
auto main_command_buffer = _factory->queue_temporary_command_buffer();
main_command_buffer.begin(vk::CommandBufferBeginInfo{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
_profiler.start(main_command_buffer);
ON_EXIT {
......@@ -97,9 +127,9 @@ namespace mirrage::renderer {