From 08ced011c649d02a3652b4131f572b90e3b011fd Mon Sep 17 00:00:00 2001
From: Andrew Golovashevich <landgrafhomyak@gmail.com>
Date: Mon, 9 Dec 2024 07:35:04 +0300
Subject: [PATCH] Utilities module for Qt, default voxel drawer and minor
 improvements

---
 CMakeLists.txt                                |   7 +-
 qt-utilities/CMakeLists.txt                   |   3 +
 .../qt_utilities/owned_qimage.hpp             |  37 +++
 .../qt_utilities/renderer_widget.hpp          |  22 ++
 .../separate_threaded_renderer.hpp            | 143 +++++++++
 .../src/separate_threaded_renderer.cpp        |   4 +
 .../renderer_api/voxel_drawer.hpp             |  14 +-
 utilities/CMakeLists.txt                      |   2 +-
 .../utilities/color.hpp                       |  38 +++
 .../utilities/default_renderer_linear.hpp     | 300 ++++++++++++++++++
 .../utilities/matrix.hpp                      |  41 ++-
 utilities/src/memory_pages_managment.cpp      |  29 ++
 12 files changed, 619 insertions(+), 21 deletions(-)
 create mode 100644 qt-utilities/CMakeLists.txt
 create mode 100644 qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/owned_qimage.hpp
 create mode 100644 qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/renderer_widget.hpp
 create mode 100644 qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/separate_threaded_renderer.hpp
 create mode 100644 qt-utilities/src/separate_threaded_renderer.cpp
 create mode 100644 utilities/include/bgtu/computer_graphics_lab_work/utilities/color.hpp
 create mode 100644 utilities/include/bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp
 create mode 100644 utilities/src/memory_pages_managment.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9eb2612..890d8e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,9 @@
-cmake_minimum_required(VERSION 3.29)
+cmake_minimum_required(VERSION 3.25)
 project(cg1)
 
 set(CMAKE_CXX_STANDARD 20)
 
-find_package(Qt5Widgets REQUIRED)
+find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui)
 set(CMAKE_AUTOMOC ON)
 
 add_executable(
@@ -26,4 +26,5 @@ add_executable(
 qt5_use_modules(cg1 Widgets)
 
 add_subdirectory(renderer-api)
-add_subdirectory(utilities)
\ No newline at end of file
+add_subdirectory(utilities)
+add_subdirectory(qt-utilities)
\ No newline at end of file
diff --git a/qt-utilities/CMakeLists.txt b/qt-utilities/CMakeLists.txt
new file mode 100644
index 0000000..f805a15
--- /dev/null
+++ b/qt-utilities/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(qt_utilities OBJECT src/separate_threaded_renderer.cpp)
+target_include_directories(qt_utilities PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_link_libraries(qt_utilities PUBLIC Qt5::Core Qt5::Widgets Qt5::Gui renderer_api utilities)
\ No newline at end of file
diff --git a/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/owned_qimage.hpp b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/owned_qimage.hpp
new file mode 100644
index 0000000..c9a7af0
--- /dev/null
+++ b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/owned_qimage.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cassert>
+#include <Qt>
+#include <QObject>
+#include <QImage>
+#include <bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp>
+#include <bgtu/computer_graphics_lab_work/renderer_api/sprite.hpp>
+
+namespace BGTU::ComputerGraphicsLabWork::QtUtilities {
+	template<class sprite_data_t>
+	class OwnedQImage : public QImage, public RendererApi::VoxelDrawer::Exporter<sprite_data_t> {
+	private:
+		RendererApi::Sprite<sprite_data_t> const *owners;
+
+	public:
+		OwnedQImage(unsigned width, unsigned height) : QImage(width, height, QImage::Format_ARGB32), owners{new RendererApi::Sprite<sprite_data_t> const *[width * height]} {
+			for (RendererApi::Sprite<sprite_data_t> const *&e: this->owners) {
+				e = nullptr;
+			}
+		}
+
+		void setPixelOwner(unsigned x, unsigned y, RendererApi::Sprite<sprite_data_t> const *o) {
+			assert(x < this->width());
+			assert(y < this->height());
+
+			this->owners[y * this->width() + x] = o;
+		}
+
+		RendererApi::Sprite<sprite_data_t> const *getPixelOwner(unsigned x, unsigned y) const {
+			assert(x < this->width());
+			assert(y < this->height());
+
+			return this->owners[y * this->width() + x];
+		}
+	};
+}
\ No newline at end of file
diff --git a/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/renderer_widget.hpp b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/renderer_widget.hpp
new file mode 100644
index 0000000..30c6f5b
--- /dev/null
+++ b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/renderer_widget.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <Qt>
+#include <QObject>
+#include <QWidget>
+#include <QMutex>
+#include "owned_qimage.hpp"
+#include "separate_threaded_renderer.hpp"
+
+
+namespace BGTU::ComputerGraphicsLabWork::QtUtilities {
+	template<class sprite_data_t>
+	class RendererWidget : public QWidget {
+	Q_OBJECT
+
+	private:
+		QMutex sync;
+		OwnedQImage <sprite_data_t> *next_image;
+		OwnedQImage <sprite_data_t> *current_image;
+		SeparateThreadedRenderer
+	};
+}
\ No newline at end of file
diff --git a/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/separate_threaded_renderer.hpp b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/separate_threaded_renderer.hpp
new file mode 100644
index 0000000..42d5b61
--- /dev/null
+++ b/qt-utilities/include/bgtu/computer_graphics_lab_work/qt_utilities/separate_threaded_renderer.hpp
@@ -0,0 +1,143 @@
+#pragma once
+
+#include <Qt>
+#include <QDebug>
+#include <QObject>
+#include <QThread>
+#include <QMutex>
+#include <QElapsedTimer>
+#include <bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp>
+#include <bgtu/computer_graphics_lab_work/renderer_api/sprite.hpp>
+#include "owned_qimage.hpp"
+
+namespace BGTU::ComputerGraphicsLabWork::QtUtilities {
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	class SeparateThreadedRenderer : public QObject {
+	private:
+		struct data {
+		public:
+			RendererApi::Sprite<sprite_data_t> *const *sprites;
+			std::size_t sprites_count;
+			sprite_data_t const *sprite_data;
+			std::uint_fast64_t ms_per_frame;
+			RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t width, height;
+			RendererApi::Projection *projection;
+		};
+
+		class RendererThread : public QThread {
+		Q_OBJECT
+
+		private:
+			SeparateThreadedRenderer *owner;
+
+		public:
+			explicit inline RendererThread(SeparateThreadedRenderer *owner) : QThread{owner}, owner{owner} {};
+
+		protected:
+			void run() final;
+		};
+
+
+		volatile data next_data;
+		QMutex sync;
+		RendererThread *thread;
+		QImage *sentinel_img;
+		renderer_context_t *renderer_context;
+	public:
+		explicit SeparateThreadedRenderer(renderer_context_t *renderer_context, QObject *owner = nullptr);
+
+	public slots:
+
+		void set_frame_size(RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t width, RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t height);
+
+		void set_ms_per_frame(std::uint_fast64_t ms);
+
+		void set_sprites(RendererApi::Sprite<sprite_data_t> *const *sprites, std::size_t sprites_count);
+
+		void set_sprite_data(sprite_data_t const *data);
+
+		void set_sprites_and_data(RendererApi::Sprite<sprite_data_t> *const *sprites, std::size_t sprites_count, sprite_data_t const *data);
+
+	signals:
+
+		void frame_rendered(OwnedQImage<sprite_data_t> *img);
+	};
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::SeparateThreadedRenderer(renderer_context_t *renderer_context, QObject *owner):
+			QObject{owner},
+			sync{},
+			renderer_context{renderer_context},
+			sentinel_img{new QImage(0, 0, QImage::Format_ARGB32)},
+			thread{new RendererThread(this)},
+			next_data{
+					.sprites = nullptr,
+					.sprites_count = 0,
+					.sprite_data = nullptr,
+					.ms_per_frame = 1000 / 60,
+					.width = 0,
+					.height = 0,
+					.projection = nullptr
+			} {
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::set_frame_size(RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t width, RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t height) {
+		this->sync.lock();
+		this->next_data.width = width;
+		this->next_data.height = height;
+		this->sync.unlock();
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::set_ms_per_frame(std::uint_fast64_t ms) {
+		this->sync.lock();
+		this->next_data.ms_per_frame = ms;
+		this->sync.unlock();
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::set_sprites(RendererApi::Sprite<sprite_data_t> *const *sprites, std::size_t sprites_count) {
+		this->sync.lock();
+		this->next_data.sprites = sprites;
+		this->next_data.sprites_count = sprites_count;
+		this->sync.unlock();
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::set_sprite_data(sprite_data_t const *data) {
+		this->sync.lock();
+		this->next_data.sprite_data = data;
+		this->sync.unlock();
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::set_sprites_and_data(RendererApi::Sprite<sprite_data_t> *const *sprites, std::size_t sprites_count, sprite_data_t const *data) {
+		this->sync.lock();
+		this->next_data.sprites = sprites;
+		this->next_data.sprites_count = sprites_count;
+		this->next_data.sprite_data = data;
+		this->sync.unlock();
+	}
+
+	template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+	void SeparateThreadedRenderer<sprite_data_t, renderer_context_t, renderer_procedure>::RendererThread::run() {
+		try {
+			QElapsedTimer timer;
+			while (true) {
+				timer.start();
+				this->owner->sync.lock();
+				data cached = this->owner->next_data;
+				this->owner->sync.unlock();
+
+				auto img = new OwnedQImage<sprite_data_t>(cached.width, cached.height);
+				(this->owner->renderer_context->*renderer_procedure)(img, cached.sprite_data, cached.sprites, cached.sprites_count);
+				emit this->owner->frame_rendered(img);
+				QThread::msleep(std::max(cached.ms_per_frame - timer.elapsed(), 0));
+			}
+		} catch (std::exception const &e) {
+			qCritical() << typeid(e).name() << ": " << e.what() << "\n" << "Renderer thread died" << "\n";
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/qt-utilities/src/separate_threaded_renderer.cpp b/qt-utilities/src/separate_threaded_renderer.cpp
new file mode 100644
index 0000000..77ce9d9
--- /dev/null
+++ b/qt-utilities/src/separate_threaded_renderer.cpp
@@ -0,0 +1,4 @@
+#include <bgtu/computer_graphics_lab_work/qt_utilities/separate_threaded_renderer.hpp>
+#include <bgtu/computer_graphics_lab_work/qt_utilities/owned_qimage.hpp>
+#include <bgtu/computer_graphics_lab_work/qt_utilities/renderer_widget.hpp>
+#include <bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp>
diff --git a/renderer-api/include/bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp b/renderer-api/include/bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp
index 9aa453c..65ed5d5 100644
--- a/renderer-api/include/bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp
+++ b/renderer-api/include/bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp
@@ -7,6 +7,9 @@
 #include "point.hpp"
 
 namespace BGTU::ComputerGraphicsLabWork::RendererApi {
+	template<class>
+	class Sprite;
+
 	class VoxelDrawer {
 	public:
 		using visible_pixel_coordinate_fast_t = std::uint_fast16_t;
@@ -22,10 +25,15 @@ namespace BGTU::ComputerGraphicsLabWork::RendererApi {
 
 		[[nodiscard]] virtual visible_pixel_coordinate_fast_t height() const = 0;
 
-		virtual void update_pixel(PointI<2>::component_t x, PointI<2>::component_t y, double z, Color::Transparent c) = 0;
+		virtual void add_voxel(PointI<2>::component_t x, PointI<2>::component_t y, double z, Color::Transparent c) = 0;
 
-		inline void update_pixel(PointI<2> p, double z, Color::Transparent c) {
-			return this->update_pixel(p.x, p.y, z, c);
+		inline void add_voxel(PointI<2> p, double z, Color::Transparent c) {
+			return this->add_voxel(p.x, p.y, z, c);
 		}
+
+		template<class sprite_data_t>
+		class Exporter {
+			virtual void set_pixel(PointI<2>::component_t x, PointI<2>::component_t y, Color c, Sprite<sprite_data_t> const* owner) = 0;
+		};
 	};
 }
\ No newline at end of file
diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt
index c4e6e18..8a34f63 100644
--- a/utilities/CMakeLists.txt
+++ b/utilities/CMakeLists.txt
@@ -1,3 +1,3 @@
-add_library(utilities OBJECT src/shader.cpp)
+add_library(utilities OBJECT src/shader.cpp src/memory_pages_managment.cpp)
 target_include_directories(utilities PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
 target_link_libraries(utilities PUBLIC renderer_api)
\ No newline at end of file
diff --git a/utilities/include/bgtu/computer_graphics_lab_work/utilities/color.hpp b/utilities/include/bgtu/computer_graphics_lab_work/utilities/color.hpp
new file mode 100644
index 0000000..f8c2371
--- /dev/null
+++ b/utilities/include/bgtu/computer_graphics_lab_work/utilities/color.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <climits>
+// #include <emmintrin.h>
+#include <bgtu/computer_graphics_lab_work/renderer_api/color.hpp>
+
+
+namespace BGTU::ComputerGraphicsLabWork::Utilities {
+	RendererApi::Color apply_transparent_color(RendererApi::Color bg, RendererApi::Color::Transparent fg) {
+#if 1
+		static auto calc = [&](RendererApi::Color::component_fast_t s, RendererApi::Color::component_fast_t o) {
+			auto oo = static_cast<std::uint_fast16_t>(o) * static_cast<std::uint_fast16_t>(fg.alpha);
+			auto ss = static_cast<std::uint_fast16_t>(s) * (static_cast<std::uint_fast16_t>(255) - static_cast<std::uint_fast16_t>(fg.alpha));
+			return static_cast<std::uint_fast8_t >((oo + ss) / 256);
+		};
+		return RendererApi::Color{
+				calc(bg.red, fg.red),
+				calc(bg.green, fg.green),
+				calc(bg.blue, fg.blue)
+		};
+#else
+		static __m128i vec_256 = _mm_set_epi16(256, 256, 256, 0, 0, 0, 0, 0);
+		__m128i vec_bg = _mm_set_epi16(bg.red, bg.green, bg.blue, 0, 0, 0, 0, 0);
+		__m128i vec_fg = _mm_set_epi16(fg.red, fg.green, fg.blue, 0, 0, 0, 0, 0);
+		__m128i vec_alpha = _mm_set_epi16(fg.alpha, fg.alpha, fg.alpha, 0, 0, 0, 0, 0);
+
+		union {
+			std::uint_fast64_t packed = static_cast<std::uint_fast64_t>(_mm_cvtsi128_si64(_mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(vec_fg, vec_alpha), _mm_mullo_epi16(vec_bg, _mm_sub_epi16(vec_256, vec_alpha))), 8)));
+			std::uint16_t unpacked[3];
+		} raw;
+		return RendererApi::Color{
+				static_cast<RendererApi::Color::component_compact_t>(raw.unpacked[0]),
+				static_cast<RendererApi::Color::component_compact_t>(raw.unpacked[1]),
+				static_cast<RendererApi::Color::component_compact_t>(raw.unpacked[2])
+		};
+#endif
+	}
+}
\ No newline at end of file
diff --git a/utilities/include/bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp b/utilities/include/bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp
new file mode 100644
index 0000000..7ad53e5
--- /dev/null
+++ b/utilities/include/bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp
@@ -0,0 +1,300 @@
+#pragma once
+
+#include <cstddef>
+#include <limits>
+#include <new>
+#include <vector>
+#include <algorithm>
+#include <bgtu/computer_graphics_lab_work/renderer_api/color.hpp>
+#include <bgtu/computer_graphics_lab_work/renderer_api/point.hpp>
+#include <bgtu/computer_graphics_lab_work/renderer_api/voxel_drawer.hpp>
+#include <bgtu/computer_graphics_lab_work/renderer_api/sprite.hpp>
+#include "color.hpp"
+#include "matrix.hpp"
+
+
+namespace BGTU::ComputerGraphicsLabWork {
+	namespace Utilities {
+		class DefaultVoxelDrawerLinear : public RendererApi::VoxelDrawer {
+		private:
+			class ZElement {
+			public:
+				ZElement *next;
+				double const z;
+				RendererApi::Color::Transparent const color;
+				RendererApi::Sprite<void> *const owner;
+
+				ZElement(ZElement *next, double z, RendererApi::Color::Transparent color, RendererApi::Sprite<void> *owner) : next{next}, z{z}, color{color}, owner{owner} {}
+
+				void link_after(ZElement *new_next) {
+					new_next->next = this->next;
+					this->next = new_next;
+				}
+			};
+
+			class ZElementAllocationBuffer {
+			private:
+				union Cell {
+					ZElement allocated;
+					struct Empty {
+						Cell *next_empty;
+					} empty;
+				};
+
+
+				class Page {
+				public:
+					Page *next;
+				private:
+
+					std::size_t initialized;
+					std::size_t capacity;
+
+					Cell buffer[0];
+
+				public:
+					explicit Page(Page *next, std::size_t capacity) : next{next}, initialized{0}, capacity{capacity} {};
+
+					Cell *try_allocate();
+
+					static std::size_t calc_capacity(std::size_t bytes) {
+						return (bytes - offsetof(Page, buffer)) / sizeof(Cell);
+					};
+
+					void reset_allocations() {
+						this->initialized = 0;
+					}
+				};
+
+				Cell *next_unallocated;
+				Page *last_page;
+				Page sentinel;
+
+			public:
+				ZElementAllocationBuffer() : next_unallocated{nullptr}, sentinel{nullptr, 0}, last_page{&this->sentinel} {};
+
+			private:
+				Page *_native_extend();
+
+			public:
+				template<class ...args_t>
+				ZElement *alloc_elem(args_t...args);
+
+				void free_elem(ZElement *e);
+
+			private:
+				void _native_free_page(Page *p);
+
+			public:
+				void release_resources();
+
+				void reset_allocations();
+
+				~ZElementAllocationBuffer();
+			};
+
+
+			RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t _width;
+			RendererApi::VoxelDrawer::visible_pixel_coordinate_fast_t _height;
+
+			struct pixel_trace_metadata {
+			public:
+				pixel_trace_metadata() : nearest_z{std::numeric_limits<double>::max()}, voxels{nullptr} {};
+
+				double nearest_z;
+				ZElement *voxels;
+			} *pixels_metadata;
+
+			ZElementAllocationBuffer allocator;
+			RendererApi::Sprite<void> *current_artist;
+		public:
+			inline DefaultVoxelDrawerLinear(std::uint_fast16_t width, std::uint_fast16_t height);
+
+			~DefaultVoxelDrawerLinear();
+
+			template<class data_t>
+			void set_current_artist(RendererApi::Sprite<data_t> *sprite) {
+				this->current_artist = reinterpret_cast<RendererApi::Sprite<void> *>(sprite);
+			}
+
+			[[nodiscard]] inline std::uint_fast16_t height() const final {
+				return this->_height;
+			};
+
+			[[nodiscard]] inline std::uint_fast16_t width() const final {
+				return this->_width;
+			}
+
+		private:
+
+			pixel_trace_metadata *at(std::uint_fast16_t x, std::uint_fast16_t y) {
+				return &(this->pixels_metadata[y * this->_width + x]);
+			}
+
+		public:
+
+			void add_voxel(RendererApi::PointI<2>::component_t x, RendererApi::PointI<2>::component_t y, double z, RendererApi::Color::Transparent c) final;
+
+
+		private:
+
+
+		public:
+
+			template<class sdata_t>
+			void export_(RendererApi::Color background, RendererApi::VoxelDrawer::Exporter<sdata_t> *receiver);
+
+			void release_resources();
+		};
+
+		DefaultVoxelDrawerLinear::ZElementAllocationBuffer::Cell *DefaultVoxelDrawerLinear::ZElementAllocationBuffer::Page::try_allocate() {
+			if (this->initialized < this->capacity) {
+				Cell *cell = &(this->buffer[this->initialized++]);
+				return cell;
+			}
+			return nullptr;
+		}
+
+		template<class... args_t>
+		DefaultVoxelDrawerLinear::ZElement *DefaultVoxelDrawerLinear::ZElementAllocationBuffer::alloc_elem(args_t... args) {
+			auto cell = this->next_unallocated;
+			if (cell != nullptr) {
+				this->next_unallocated = cell->empty.next_empty;
+				return &(cell->allocated);
+			}
+
+			while ((cell = this->last_page->try_allocate()) == nullptr)
+				this->_native_extend();
+
+			return new(&(cell->allocated)) ZElement{args...};
+		}
+
+
+		void DefaultVoxelDrawerLinear::ZElementAllocationBuffer::free_elem(DefaultVoxelDrawerLinear::ZElement *e) {
+			Cell *cell = reinterpret_cast<Cell *>(e);
+			cell->empty.next_empty = this->next_unallocated;
+			this->next_unallocated = cell;
+		}
+
+		void DefaultVoxelDrawerLinear::ZElementAllocationBuffer::release_resources() {
+			Page *p;
+			while (this->last_page != nullptr) {
+				p = this->last_page;
+				this->last_page = p->next;
+				this->_native_free_page(p);
+			}
+			this->next_unallocated = nullptr;
+		}
+
+		void DefaultVoxelDrawerLinear::ZElementAllocationBuffer::reset_allocations() {
+			Page *p = this->last_page;
+			while (p != nullptr) {
+				p->reset_allocations();
+				p = p->next;
+			}
+			this->next_unallocated = nullptr;
+		}
+
+		DefaultVoxelDrawerLinear::ZElementAllocationBuffer::~ZElementAllocationBuffer() {
+			this->release_resources();
+		}
+
+
+		DefaultVoxelDrawerLinear::DefaultVoxelDrawerLinear(std::uint_fast16_t width, std::uint_fast16_t height) :
+				_width{width},
+				_height{height},
+				pixels_metadata{new pixel_trace_metadata[sizeof(ZElement) * width * height]{}},
+				allocator{},
+				current_artist{nullptr} {}
+
+		DefaultVoxelDrawerLinear::~DefaultVoxelDrawerLinear() {
+			delete[] this->pixels_metadata;
+		}
+
+		void DefaultVoxelDrawerLinear::add_voxel(RendererApi::PointI<2>::component_t x, RendererApi::PointI<2>::component_t y, double z, RendererApi::Color::Transparent c) {
+			if (x < 0 || this->_width <= x) return;
+			if (y < 0 || this->_height <= y) return;
+			if (z < 0) return;
+			auto p = this->at(x, y);
+			if (z <= p->nearest_z) {
+				p->nearest_z = z;
+#if 1
+				if (c.alpha == 255) {
+					ZElement *e;
+					while (p->voxels != nullptr) {
+						e = p->voxels;
+						p->voxels = e->next;
+						this->allocator.free_elem(e);
+					}
+				}
+#endif
+			}
+			p->voxels = this->allocator.alloc_elem(p->voxels, z, c, this->current_artist);
+		}
+
+		void DefaultVoxelDrawerLinear::release_resources() {
+			this->allocator.release_resources();
+		}
+
+		template<class sdata_t>
+		void DefaultVoxelDrawerLinear::export_(RendererApi::Color background, RendererApi::VoxelDrawer::Exporter<sdata_t> *receiver) {
+			std::vector<ZElement *> sorter;
+			std::uint_fast16_t y = 0;
+			std::uint_fast16_t x = 0;
+
+			for (std::int_fast64_t i = this->_width * this->_height - 1; i >= 0; i--) {
+				for (ZElement *e = this->pixels_metadata[i].voxels; e != nullptr; e = e->next) {
+					sorter.push_back(e);
+				}
+				std::sort(sorter.rbegin(), sorter.rend(), [](ZElement *l, ZElement *r) { return l->z < r->z; });
+				RendererApi::Color p = background;
+				for (auto a: sorter) {
+					p = apply_transparent_color(p, a->color);
+				}
+				receiver->export_pixel(x, y, p, sorter[sorter.size() - 1]->owner);
+				if (++x >= this->_width) {
+					y++;
+					x = 0;
+				}
+			}
+		}
+	}
+
+	namespace QtUtilities {
+		template<class sprite_data_t, class renderer_context_t, void (renderer_context_t::*renderer_procedure)(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t)>
+		class SeparateThreadedRenderer;
+
+#if 0
+		template<class sprite_data_t, bool _private_param>
+		class _SeparateThreadedDefaultRendererLinear {
+		};
+
+		template<class sprite_data_t>
+		class _SeparateThreadedDefaultRendererLinear<sprite_data_t, false> {
+			template<class, bool>
+			friend
+			class _SeparateThreadedDefaultRendererLinear;
+
+
+		private:
+			_SeparateThreadedDefaultRendererLinear() = delete;
+
+			class Context : public Utilities::DefaultVoxelDrawerLinear<sprite_data_t> {
+
+			};
+
+		};
+
+
+		class _SeparateThreadedDefaultRendererLinear : public SeparateThreadedRenderer<sprite_data_t, SeparateThreadedDefaultRendererLinear < sprite_data_t>, &SeparateThreadedDefaultRendererLinear<sprite_data_t>::render
+
+		> {
+		public:
+
+		void render(RendererApi::VoxelDrawer::Exporter<sprite_data_t> *, sprite_data_t *, RendererApi::Sprite<sprite_data_t> *const *, std::size_t) {
+
+		}
+	};
+#endif
+	}
+}
\ No newline at end of file
diff --git a/utilities/include/bgtu/computer_graphics_lab_work/utilities/matrix.hpp b/utilities/include/bgtu/computer_graphics_lab_work/utilities/matrix.hpp
index bf911c7..7ae510b 100644
--- a/utilities/include/bgtu/computer_graphics_lab_work/utilities/matrix.hpp
+++ b/utilities/include/bgtu/computer_graphics_lab_work/utilities/matrix.hpp
@@ -17,11 +17,9 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities {
 				elem_t e20, elem_t e21, elem_t e22
 		) : data{e00, e01, e02, e10, e11, e12, e20, e21, e22} {}
 
-		static inline const Matrix3<elem_t> I{1, 0, 0,
-											  0, 1, 0,
-											  0, 0, 1};
+		static const Matrix3<elem_t> I;
 	private:
-		static inline const Matrix3<elem_t> _tmp{};
+		static const Matrix3<elem_t> _tmp;
 
 
 		template<class operator_t>
@@ -143,17 +141,14 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities {
 				elem_t e30, elem_t e31, elem_t e32, elem_t e33
 		) : data{e00, e01, e02, e03, e10, e11, e12, e13, e20, e21, e22, e23, e30, e31, e32, e33} {}
 
-		static inline const Matrix4<elem_t> I{1, 0, 0, 0,
-											  0, 1, 0, 0,
-											  0, 0, 1, 0,
-											  0, 0, 0, 1};
+		static const Matrix4<elem_t> I;
 
 	private:
-		static inline const Matrix4<elem_t> _tmp{};
+		static const Matrix4<elem_t> _tmp;
 
 		template<class operator_t>
 		Matrix4<elem_t> _zipmap_matrix(Matrix4<elem_t> const *other, operator_t op) const {
-			Matrix4<elem_t> dst;
+			Matrix4 < elem_t > dst;
 			auto _map_row = [&](std::size_t row_index) {
 				dst.data[row_index][0] = op(row_index, 0, this->data[row_index][0], other->data[row_index][0]);
 				dst.data[row_index][1] = op(row_index, 1, this->data[row_index][1], other->data[row_index][1]);
@@ -216,10 +211,10 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities {
 		}
 
 		static Matrix4<elem_t> scale(elem_t x, elem_t y, elem_t z) {
-			return Matrix4<elem_t>{x, 0, 0, 0,
-								   0, y, 0, 0,
-								   0, 0, z, 0,
-								   0, 0, 0, 1};
+			return Matrix4 < elem_t > {x, 0, 0, 0,
+									   0, y, 0, 0,
+									   0, 0, z, 0,
+									   0, 0, 0, 1};
 		}
 
 
@@ -266,6 +261,24 @@ namespace BGTU::ComputerGraphicsLabWork::Utilities {
 		}
 	};
 
+	template<class elem_t>
+	const Matrix3<elem_t> Matrix3<elem_t>::I{1, 0, 0, 0,
+											 0, 1, 0, 0,
+											 0, 0, 1, 0,
+											 0, 0, 0, 1};
+
+	template<class elem_t>
+	const Matrix3<elem_t> Matrix3<elem_t>::_tmp{};
+
+	template<class elem_t>
+	const Matrix4<elem_t> Matrix4<elem_t>::I{1, 0, 0, 0,
+											 0, 1, 0, 0,
+											 0, 0, 1, 0,
+											 0, 0, 0, 1};
+
+	template<class elem_t>
+	const Matrix4<elem_t> Matrix4<elem_t>::_tmp{};
+
 	using Matrix4i = Matrix4<int>;
 	using Matrix4l = Matrix4<long>;
 	using Matrix4f = Matrix4<float>;
diff --git a/utilities/src/memory_pages_managment.cpp b/utilities/src/memory_pages_managment.cpp
new file mode 100644
index 0000000..d929a60
--- /dev/null
+++ b/utilities/src/memory_pages_managment.cpp
@@ -0,0 +1,29 @@
+#include <bgtu/computer_graphics_lab_work/utilities/default_renderer_linear.hpp>
+
+#if defined(_WIN32)
+
+#include <cstdlib>
+#include <cassert>
+#include <new>
+#include <Windows.h>
+
+namespace BGTU::ComputerGraphicsLabWork::Utilities {
+	DefaultVoxelDrawerLinear::ZElementAllocationBuffer::Page *DefaultVoxelDrawerLinear::ZElementAllocationBuffer::_native_extend() {
+		SYSTEM_INFO si;
+		GetSystemInfo(&si);
+
+		std::size_t size = max(si.dwPageSize, si.dwAllocationGranularity);
+
+		void *raw = VirtualAlloc(nullptr, max(si.dwPageSize, si.dwAllocationGranularity), MEM_COMMIT, PAGE_READWRITE);
+		if (raw != nullptr) throw std::bad_alloc{};
+		return this->last_page = new(raw)Page(this->last_page, Page::calc_capacity(size));
+	}
+
+
+	void DefaultVoxelDrawerLinear::ZElementAllocationBuffer::_native_free_page(DefaultVoxelDrawerLinear::ZElementAllocationBuffer::Page *p) {
+		VirtualFree(p, 0, MEM_DECOMMIT | MEM_RELEASE);
+	}
+}
+#else
+# error ""
+#endif
\ No newline at end of file