#pragma once #include "Client/ServerSession.hpp" #include "Common/Async.hpp" #include #include #include #include #include #include #include #include #include #include #include #define HAS_IMGUI #include "freetype/freetype.h" #include #include #include #define TOS_VULKAN_NO_VIDEO #include #define GLFW_INCLUDE_NONE #include static_assert(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO); #define IMGUI_ENABLE_STB_TEXTEDIT_UNICODE namespace LV::Client::VK { class VulkanRenderSession; using namespace TOS; ByteBuffer loadPNG(std::ifstream &&file, int &width, int &height, bool &hasAlpha, bool flipOver = true); ByteBuffer loadPNG(std::istream &&file, int &width, int &height, bool &hasAlpha, bool flipOver = true); struct DeviceId { uint32_t VendorId = -1, DeviceId = -1; }; struct Settings { DeviceId DeviceMain; uint32_t QueueGraphics = -1, QueueSurface = -1; bool Debug = true; bool isValid() { return !(DeviceMain.VendorId == -1 || DeviceMain.DeviceId == -1 || QueueGraphics == -1 || QueueSurface == -1); } }; class ServerObj; class DescriptorLayout; class Pipeline; class DescriptorPool; class ShaderModule; class IVulkanDependent; class Buffer; #define vkAssert(err) if(!bool(err)) { MAKE_ERROR(__FILE__ << ": " << __LINE__ << "//" << __func__); } /* Vulkan.getSettingsNext() = Vulkan.getBestSettings(); Vulkan.reInit(); */ class Vulkan : public AsyncObject { private: Logger LOG = "Vulkan"; struct vkInstanceLayer { std::string LayerName = "nullptr", Description = "nullptr"; uint32_t SpecVersion = -2, ImplementationVersion = -2; bool operator==(const vkInstanceLayer &obj) { return LayerName == obj.LayerName; } std::string toString() { return (std::stringstream() << "Имя слоя: " << LayerName << "\nОписание: " << Description << "\nСпец версия: " << SpecVersion << "\nВерсия реализации: " << ImplementationVersion).str(); } }; const std::vector NeedExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, "VK_KHR_shader_float16_int8", "VK_KHR_16bit_storage" }; struct vkInstanceExtension { std::string ExtensionName = "nullptr"; uint32_t SpecVersion = -2; bool operator==(const vkInstanceExtension &obj) { return ExtensionName == obj.ExtensionName; } std::string toString() { return (std::stringstream() << "Имя расширения: " << ExtensionName << "\nСпец версия: " << SpecVersion).str(); } }; struct vkDeviceExtension { std::string ExtensionName = "nullptr"; uint32_t SpecVersion = -2; }; struct vkDevice { uint32_t ApiVersion; uint32_t DriverVersion; uint32_t VendorID; uint32_t DeviceID; VkPhysicalDeviceType DeviceType; std::string DeviceName; //int128_t PipelineCacheUUID[VK_UUID_SIZE]; VkPhysicalDeviceLimits Limits; VkPhysicalDeviceSparseProperties SparseProperties; VkPhysicalDeviceFeatures DeviceFeatures; std::vector Extensions; std::vector FamilyProperties; static std::string getTypeAsString(VkPhysicalDeviceType type) { switch(type) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: return "Неизвестно"; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: return "ВСТРОЕННЫЙ ГРАФИЧЕСКИЙ процессор"; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: return "ДИСКРЕТНЫЙ ГРАФИЧЕСКИЙ процессор"; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: return "ВИРТУАЛЬНЫЙ ГРАФИЧЕСКИЙ процессор"; case VK_PHYSICAL_DEVICE_TYPE_CPU: return "ЦП"; default: return "Вышли за рамки определений VkPhysicalDeviceType"; } } std::string getDeviceName() { return (std::stringstream() << "Имя устройства: " << DeviceName << "\nТип устройства: " << getTypeAsString(DeviceType) << "\nId поставщика: " << VendorID << "\nId устройства: " << DeviceID).str(); } }; class vkInstance { Vulkan *Handler; VkInstance Instance = nullptr; public: vkInstance(Vulkan *handler, std::vector layers = {}, std::vector extensions = {}); ~vkInstance(); Vulkan* getHandler() const { return Handler; } VkInstance getInstance() const { return Instance; } }; struct SwapchainBuffers { VkImage Image = nullptr; VkImageView View = nullptr; VkCommandBuffer Cmd = nullptr; VkFramebuffer FrameBuffer = nullptr; }; bool NeedShutdown = false; asio::executor_work_guard GuardLock; public: struct { std::vector InstanceLayers; std::vector InstanceExtensions; std::vector GLFWExtensions; std::vector Devices; std::optional Instance; GLFWwindow *Window = nullptr; VkSurfaceKHR Surface = nullptr; VkPhysicalDevice PhysicalDevice = nullptr; VkPhysicalDeviceMemoryProperties DeviceMemoryProperties = {0}; VkDevice Device = nullptr; VkQueue DeviceQueueGraphic = VK_NULL_HANDLE; VkFormat SurfaceFormat = VK_FORMAT_B8G8R8A8_UNORM; VkColorSpaceKHR SurfaceColorSpace = VK_COLOR_SPACE_MAX_ENUM_KHR; VkCommandPool Pool = VK_NULL_HANDLE; VkCommandBuffer CommandBufferData = VK_NULL_HANDLE; VkCommandBuffer CommandBufferRender = VK_NULL_HANDLE; // RenderPass для экранного буфера VkRenderPass RenderPass = VK_NULL_HANDLE; VkSwapchainKHR Swapchain = VK_NULL_HANDLE; std::vector DrawBuffers; struct { VkImage Image = VK_NULL_HANDLE; VkImageView View = VK_NULL_HANDLE; VkDeviceMemory Memory = VK_NULL_HANDLE; VkFramebuffer Frame = VK_NULL_HANDLE; } InlineTexture; uint32_t // Количество сменяемых буферов DrawBufferCount = 0, // Текущий рабочий буфер DrawBufferCurrent = 0; std::string SwapchainChangeReport; const VkFormat DepthFormat = VK_FORMAT_D32_SFLOAT; VkImage DepthImage = VK_NULL_HANDLE; VkImageView DepthView = VK_NULL_HANDLE; VkDeviceMemory DepthMemory = VK_NULL_HANDLE; VkDescriptorPool ImGuiDescPool = VK_NULL_HANDLE; // Идентификатор потока графики std::thread::id ThisThread = std::this_thread::get_id(); } Graphics; enum struct DrawState { Begin, Drawing, End }; struct { uint32_t Width = 960, Height = 540; VkExtent2D FrameExtent; std::mutex BeforeDrawMtx; std::queue> BeforeDraw; DrawState State = DrawState::Begin; } Screen; struct { DestroyLock UseLock; std::thread MainThread; std::shared_ptr RSession; std::unique_ptr Session; std::list ImGuiInterfaces; std::unique_ptr Server; double MLastPosX, MLastPosY; } Game; private: Logger LOGGER = "Vulkan"; Settings // Текущие настройки SettingsState, // Новые настройки, будут применены после вызова reInit SettingsNext, // Наилучшие настройки, расчитываются единожды SettingsBest; std::optional LibraryVulkan; std::queue> VulkanContext; // Объекты рисовки std::unordered_set> ROS_Dependents; friend DescriptorLayout; friend Pipeline; friend ShaderModule; friend DescriptorPool; friend Buffer; void updateResources(); public: // Блокировка рендера кадра //SharedMutex MutexRender; private: // Обработчик рендера void run(); // Проверка и инициализация библиотеки Vulkan void checkLibrary(); static void glfwCallbackOnResize(GLFWwindow *window, int width, int height); static void glfwCallbackOnMouseButton(GLFWwindow* window, int button, int action, int mods); static void glfwCallbackOnCursorPos(GLFWwindow* window, double xpos, double ypos); static void glfwCallbackOnScale(GLFWwindow* window, float xscale, float yscale); static void glfwCallbackOnKey(GLFWwindow* window, int key, int scancode, int action, int mods); static void glfwCallbackOnFocus(GLFWwindow* window, int focused); // Применение новых настроек void initNextSettings(); // Деинициализация вулкана вплоть до удаления instance void deInitVulkan(); // Обработчик ошибок GLFW void glfwCallbackError(int error, const char *description); // Освобождение объектов цепочек рендера void freeSwapchains(); // Создание цепочек рендера (при изменении размера окна) void buildSwapchains(); public: Vulkan(asio::io_context &ioc); ~Vulkan(); Vulkan(const Vulkan&) = delete; Vulkan(Vulkan&&) = delete; Vulkan& operator=(const Vulkan&) = delete; Vulkan& operator=(Vulkan&&) = delete; // Расчитывает оптимальные настройки для местного оборудования Settings getBestSettings(); /* Переинициализация всех объектов вулкан в соответствии * с новыми настройками */ void reInit(); /* Если при перестройки под новые настройки понадобится полное * пересоздание контекста Vulkan - вернёт true */ bool needFullVulkanRebuild(); // Поиск подходящей памяти uint32_t memoryTypeFromProperties(uint32_t bitsOfAcceptableTypes, VkFlags bitsOfAccessMask); // Принудительно выполнить команды буфера void flushCommandBufferData(); /* Части графических конвейеров Удалить можно только после полной деинициализации вулкана */ // Загрузка шейдера std::shared_ptr createShader(std::string_view data); // Регистрация объекта зависимого от изменений в графическом конвейере void registerDependent(std::shared_ptr dependent); // Регистрация объекта зависимого от изменений в графическом конвейере Vulkan& operator<<(std::shared_ptr dependent); // Lock: MutexRender // Возвращает настройки текущие настройки const Settings& getSettings() { return SettingsState; } // Возвращает настройки заготовленные настройки Settings& getSettingsNext() { return SettingsNext; } bool isAlive() { return false; } // Добавить обработчик перед началом рисовки кадра void beforeDraw(std::function &&callback) { std::lock_guard lock(Screen.BeforeDrawMtx); Screen.BeforeDraw.push(std::move(callback)); } bool isRenderThread() { return std::this_thread::get_id() == Graphics.ThisThread; } void addImGUIFont(std::string_view view); void gui_MainMenu(); void gui_ConnectedToServer(); }; class IVulkanDependent : public std::enable_shared_from_this { protected: virtual void free(Vulkan *instance) = 0; virtual void init(Vulkan *instance) = 0; friend Vulkan; friend Pipeline; public: IVulkanDependent() = default; virtual ~IVulkanDependent(); IVulkanDependent(const IVulkanDependent&) = delete; IVulkanDependent(IVulkanDependent&&) = delete; IVulkanDependent& operator=(const IVulkanDependent&) = delete; IVulkanDependent& operator=(IVulkanDependent&&) = delete; }; /* Разметка данных на входе шейдера (юниформы), и разметка сетов дексрипторов */ class DescriptorLayout : public IVulkanDependent { protected: std::vector ShaderLayoutBindings; std::vector ShaderPushConstants; VkDescriptorSetLayout DescLayout = VK_NULL_HANDLE; VkPipelineLayout Layout = VK_NULL_HANDLE; protected: virtual void init(Vulkan *instance) override; virtual void free(Vulkan *instance) override; friend Pipeline; public: DescriptorLayout(const std::vector &layout = {}, const std::vector &pushConstants = {}); virtual ~DescriptorLayout(); DescriptorLayout(const DescriptorLayout&) = delete; DescriptorLayout(DescriptorLayout&&) = delete; DescriptorLayout& operator=(const DescriptorLayout&) = delete; DescriptorLayout& operator=(DescriptorLayout&&) = delete; operator VkDescriptorSetLayout() const { return DescLayout; } operator VkPipelineLayout() const { return Layout; } }; class Pipeline : public IVulkanDependent { std::string PipelineInfo; protected: VkPipeline PipelineObj = VK_NULL_HANDLE; VkPrimitiveTopology Topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; protected: struct { // Настройка формата вершин шейдера std::vector ShaderVertexBindings; std::vector ShaderVertexAttribute; // Настройка входных дескрипторов шейдера std::shared_ptr ShaderLayoutBindings; // Конвейер шейдеров std::vector ShaderStages; // Настройки растеризатора VkPipelineRasterizationStateCreateInfo Rasterization; // Настройка мультисемплинга VkPipelineMultisampleStateCreateInfo Multisample; // Настройка смешивания цветов std::vector ColorBlend; float ColorBlendConstants[4]; VkLogicOp ColorBlendLogicOp; // Тест буфера глубины и трафарета VkPipelineDepthStencilStateCreateInfo DepthStencil; // Динамичные состояния std::vector DynamicStates; uint32_t Subpass = 0; } Settings; virtual void free(Vulkan *instance) override; virtual void init(Vulkan *instance) override; public: Pipeline(std::shared_ptr layout); virtual ~Pipeline(); Pipeline(const Pipeline&) = delete; Pipeline(Pipeline&&) = delete; Pipeline& operator=(const Pipeline&) = delete; Pipeline& operator=(Pipeline&&) = delete; void bind(Vulkan *instance) { vkCmdBindPipeline(instance->Graphics.CommandBufferRender, VK_PIPELINE_BIND_POINT_GRAPHICS, PipelineObj); } void bindDescriptorSets(Vulkan *instance, VkDescriptorSet *sets, size_t setCount = 1, size_t offset = 0) { vkCmdBindDescriptorSets(instance->Graphics.CommandBufferRender, VK_PIPELINE_BIND_POINT_GRAPHICS, *Settings.ShaderLayoutBindings, offset, setCount, sets, 0, nullptr); } std::string getPipelineInfo() { return PipelineInfo; } }; /* Шейдер загружаемый из предкомпилированных файлов, планируется, что он будет существовать до конца работы игры */ class ShaderModule : public IVulkanDependent { VkShaderModule Module = VK_NULL_HANDLE; std::string Source; protected: virtual void free(Vulkan *instance) override; virtual void init(Vulkan *instance) override; public: ShaderModule(std::string_view view); virtual ~ShaderModule(); VkShaderModule getModule() const { return Module; } operator VkShaderModule() const { return Module; } }; /* Используются для хранения вершинных данных, доступны к редактированию на хосте Не принимает событий от вулкана */ class Buffer { Vulkan *Instance = VK_NULL_HANDLE; VkBuffer Buff = VK_NULL_HANDLE; VkDeviceMemory Memory = VK_NULL_HANDLE; VkDeviceSize Size = 0; public: Buffer(Vulkan *instance, VkDeviceSize bufferSize, VkBufferUsageFlags usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); virtual ~Buffer(); Buffer(const Buffer&) = delete; Buffer(Buffer&&); Buffer& operator=(const Buffer&) = delete; Buffer& operator=(Buffer&&); VkBuffer getBuffer() const { return Buff; } operator VkBuffer() const { return Buff; } VkDeviceMemory getMemory() const { return Memory; } operator VkDeviceMemory() const { return Memory; } VkDeviceSize getSize() const { return Size; } operator VkDeviceSize() const { return Size; } operator VkDescriptorBufferInfo() const { return {Buff, 0, Size}; } /* Размещает данные в памяти хоста для их редактирования и возврата драйверу через unMapMemory Смещение, Размер, флаги */ uint8_t* mapMemory(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags = 0) const; /* Размещает данные в памяти хоста для их редактирования и возврата драйверу через unMapMemory */ uint8_t* mapMemory() const { return mapMemory(0, Size, 0); } /* Возвращает память под управление драйверу */ void unMapMemory() const; }; /* Буфер комманд. */ class CommandBuffer { VkCommandBuffer Buffer = VK_NULL_HANDLE; Vulkan *Instance; std::vector> AfterExecute; VkCommandPool OffthreadPool = VK_NULL_HANDLE; public: CommandBuffer(Vulkan *instance); ~CommandBuffer(); CommandBuffer(const CommandBuffer&) = delete; CommandBuffer(CommandBuffer&&) = delete; CommandBuffer& operator=(const CommandBuffer&) = delete; CommandBuffer& operator=(CommandBuffer&&) = delete; VkCommandBuffer getBuffer() { return Buffer; } operator VkCommandBuffer() { return Buffer; } // Выполнить команды, после вернуть управление void execute(); // Начать выполнение команд без ожидания void executeNoAwait(); // Выполнить после выполнения буфера void afterExecute(const std::function &&callback) { AfterExecute.push_back(std::move(callback)); } }; class SimpleImage { Vulkan *Instance; VkSampler Sampler = VK_NULL_HANDLE; VkImage Image = VK_NULL_HANDLE; VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM; VkDeviceMemory Memory = VK_NULL_HANDLE; VkImageView View = VK_NULL_HANDLE; size_t Width = 0, Height = 0; void postInit(const ByteBuffer &pixels, size_t width, size_t height); public: SimpleImage(Vulkan *instance, std::filesystem::path filePNG); SimpleImage(Vulkan *instance, const ByteBuffer &pixels, size_t width, size_t height); virtual ~SimpleImage(); VkSampler getSampler() const { return Sampler; } operator VkSampler() const { return Sampler; } VkImage getImage() const { return Image; } operator VkImage() const { return Image; } VkImageLayout getImageLayout() const { return ImageLayout; } operator VkImageLayout() const { return ImageLayout; } VkDeviceMemory getMemory() const { return Memory; } VkImageView getView() const { return View; } operator VkImageView() const { return View; } operator VkDescriptorImageInfo() const { return {Sampler, View, ImageLayout}; } size_t getWidth() const { return Width; } size_t getHeight() const { return Height; } static ByteBuffer loadTexture(std::filesystem::path file, size_t &width, size_t &height, bool &alpha); }; /* Картинка, которую можно обновлять перед кадром */ class DynamicImage : public std::enable_shared_from_this { protected: Vulkan *Instance; VkSampler Sampler = VK_NULL_HANDLE; VkImage Image = VK_NULL_HANDLE; VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM; VkDeviceMemory Memory = VK_NULL_HANDLE; VkImageView View = VK_NULL_HANDLE; uint16_t Width = 0, Height = 0; bool IsFirstCreate = true; std::vector> AfterRecreate; void changeSampler(const VkSamplerCreateInfo *sampler); public: DynamicImage(Vulkan *instance, uint32_t width = 0, uint32_t height = 0, const uint32_t *rgba = nullptr); virtual ~DynamicImage(); DynamicImage(const DynamicImage&) = delete; DynamicImage(DynamicImage&&) = delete; DynamicImage& operator=(const DynamicImage&) = delete; DynamicImage& operator=(DynamicImage&&) = delete; /* Пересоздаёт картинку с новым размером и данными */ void recreateImage(uint16_t width, uint16_t height, const uint32_t *rgba); /* Обновляет изображение */ void changeData(const uint32_t *rgba); void changeData(int32_t x, int32_t y, uint16_t width, uint16_t height, const uint32_t *rgba); /* Добыть изображение из текстуры */ void readData(int32_t x, int32_t y, uint16_t width, uint16_t height, uint32_t *rgba); VkSampler getSampler() const { return Sampler; } operator VkSampler() const { return Sampler; } VkImage getImage() const { return Image; } operator VkImage() const { return Image; } VkImageLayout getImageLayout() const { return ImageLayout; } operator VkImageLayout() const { return ImageLayout; } VkDeviceMemory getMemory() const { return Memory; } VkImageView getView() const { return View; } operator VkImageView() const { return View; } operator VkDescriptorImageInfo() const { return {Sampler, View, ImageLayout}; } size_t getWidth() const { return Width; } size_t getHeight() const { return Height; } /* Если картинка будет пересоздана, то после будет вызван обработчик Если обработчик больше не хочет обрабатывать, пусть вернёт false */ void addCallbackOnImageDescriptorChange(std::function &&callback); }; class AtlasImage : public DynamicImage { public: struct alignas(16) InfoSubTexture { uint32_t isExist : 1 = 0, _ : 31 = 0; uint16_t PosX = 0, PosY = 0, Width = 0, Height = 0; struct { uint16_t Enabled : 1 = 0, Frames : 15 = 0; uint16_t TimePerFrame = 0; } Animation; }; protected: // Текущие значения в атласе std::map SubTextures; std::mutex Changes; // Если нужно перераспределить текстуры bool NeedRebuild = false; // Если нужно перезалить uniform буфер с разметкой атласа bool NeedUpdateSchema = false; // Высоты занятости. Текстуры можно добавлять на полотно без полного пересчёта std::vector Heights; // Свободные идентификаторы std::vector FreeIds; // Выделить место под текстуру void claimTexture(uint16_t id, uint16_t width, uint16_t height); // По возможности сократить количество свободных идентификаторов void optimizeFreeIds(); uint8_t IdsChanges = 0; // Здесь хранятся текстуры в чистом виде, их преоритет выше, чем у данных в атласе // Если есть объект без данных, значит под текстуру ещё не выделено место в атласе std::map CachedData; // Если uniform буфер пересоздан std::vector> AfterUniformChange; // Буфер со схемой VK::Buffer UniformSchema, HostBuffer; struct UniformInfo { // Количество текстур uint16_t SubsCount; uint16_t _; // Счётчик времени с разрешением 8 бит в секунду uint32_t Counter; uint16_t Width, Height; //std::vector SubsInfo; }; public: AtlasImage(Vulkan *instance); virtual ~AtlasImage(); AtlasImage(const AtlasImage&) = delete; AtlasImage(AtlasImage&&) = delete; AtlasImage& operator=(const AtlasImage&) = delete; AtlasImage& operator=(AtlasImage&&) = delete; // Выделить новую текстуру в атласе uint16_t atlasAddTexture(uint16_t width, uint16_t height); // Удалить текстуру void atlasRemoveTexture(uint16_t id); // Изменить размер текстуры void atlasResizeTexture(uint16_t id, uint16_t width, uint16_t height); // Изменить данные текстуры void atlasChangeTextureData(uint16_t id, const uint32_t *rgba); // Устанавливает анимацию на текстуру, frames max 2^15 void atlasSetAnimation(uint16_t id, bool enabled, uint16_t frames = 0, float timePerFrame = 0); // Вычищает все текстуры void atlasClear(); // Срабатывает при изменении буфера разметки данных атласа void atlasAddCallbackOnUniformChange(std::function &&callback); // Обновить динамичные данные перед каждым кадром (для анимаций) void atlasUpdateDynamicData(); // Создаёт схему из атласа, сохраняет текстуры атласа и их положения ByteBuffer atlasSchemaUnload() const; // Восстанавливает атлас по схеме void atlasSchemaLoad(const ByteBuffer &buff); operator VkDescriptorBufferInfo() const { return VkDescriptorBufferInfo { .buffer = UniformSchema, .offset = 0, .range = UniformSchema.getSize() }; } // Вернёт указатель на информацию, если она есть const InfoSubTexture* atlasGetTextureInfo(uint16_t id); }; // Массив двумерных текстур одного размера class ArrayImage { Vulkan *Instance; VkSampler Sampler = VK_NULL_HANDLE; VkImage Image = VK_NULL_HANDLE; VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM; VkDeviceMemory Memory = VK_NULL_HANDLE; VkImageView View = VK_NULL_HANDLE; uint32_t Width = 0; std::map TextureMapping; public: ArrayImage(Vulkan *instance, std::filesystem::path directory); ~ArrayImage(); /* Возвращает индекс текстуры в массиве по названию её файла без расширения */ uint16_t getTextureId(const std::string &name); VkSampler getSampler() const { return Sampler; } operator VkSampler() const { return Sampler; } VkImage getImage() const { return Image; } operator VkImage() const { return Image; } VkImageLayout getImageLayout() const { return ImageLayout; } operator VkImageLayout() const { return ImageLayout; } VkDeviceMemory getMemory() const { return Memory; } VkImageView getView() const { return View; } operator VkImageView() const { return View; } operator VkDescriptorImageInfo() const { return {Sampler, View, ImageLayout}; } size_t getWidth() const { return Width; } }; /* Накопитель вершин на время кадра, для динамично вычисляемых вершин на время одного RenderPass */ template class VertexFrameCapacitor { Vulkan *Instance; std::vector Buffers; // Всегда должен быть какой-нибудь буфер Vertex *LastData = nullptr; size_t Offset = 0; int Counter = 0; const uint32_t CountVertexPerBuffer = 300000; // Делится и на 4 3 и на 2 public: VertexFrameCapacitor(Vulkan *instance) : Instance(instance) { Buffers.emplace_back(instance, CountVertexPerBuffer*sizeof(Vertex)); } ~VertexFrameCapacitor() { beforeStartDraw(); } // Добавить список вершин void pushVertexs(const Vertex *vertexs, size_t count) { while(count) { // Текущая позиция в цепочке буферов size_t mainOffset = Offset + Counter; // Буфер на котором ведётся запись size_t bufferIndex = 0; for(; bufferIndex < Buffers.size(); bufferIndex++) if(Buffers[bufferIndex].getSize() / sizeof(Vertex) > mainOffset) break; else mainOffset -= Buffers[bufferIndex].getSize() / sizeof(Vertex); // Буферы кончились if(bufferIndex >= Buffers.size()) { if(LastData) Buffers.back().unMapMemory(); LastData = nullptr; Buffers.emplace_back(Instance, CountVertexPerBuffer*sizeof(Vertex)); continue; } // Сколько места доступно для вершин size_t freeCount = Buffers[bufferIndex].getSize()/sizeof(Vertex)-mainOffset; // Место кончилось vkAssert(freeCount && "Место всегда должно быть"); // if(!freeCount) // { // if(LastData) // Buffers[bufferIndex].unMapMemory(); // LastData = nullptr; // Buffers.emplace_back(Instance, CountVertexPerBuffer*sizeof(Vertex)); // continue; // } // Сколько будем записывать вершин size_t writeCount = std::min(count, freeCount); // Запишем данные count -= writeCount; Counter += writeCount; if(!LastData) LastData = (Vertex*) Buffers[bufferIndex].mapMemory(mainOffset*sizeof(Vertex), Buffers[bufferIndex].getSize()-mainOffset*sizeof(Vertex)); std::copy(vertexs, vertexs+writeCount, LastData); vertexs += writeCount; LastData += writeCount; // Место в буфере кончилось if(count || freeCount == writeCount) { Buffers[bufferIndex].unMapMemory(); LastData = nullptr; } } } // Добавить список вершин void pushVertexs(const std::vector &vertexs) { pushVertexs(vertexs.data(), vertexs.size()); } VertexFrameCapacitor& operator<<(const std::vector &vertexs) { pushVertexs(vertexs); return *this; } // Добавить вершину void pushVertex(const Vertex &vertex) { pushVertexs(&vertex, 1); } VertexFrameCapacitor& operator<<(const Vertex &vertex) { pushVertex(vertex); return *this; } // Нарисовать то, что загрузили void draw(VkCommandBuffer &renderCmd) { VkDeviceSize vkOffsets = 0; while(Counter) { // Ищем буфер на котором начата запись size_t offset = Offset; size_t bufferIndex = 0; for(; bufferIndex < Buffers.size(); bufferIndex++) if(Buffers[bufferIndex].getSize() / sizeof(Vertex) > offset) break; else offset -= Buffers[bufferIndex].getSize() / sizeof(Vertex); VkBuffer buff = Buffers[bufferIndex].getBuffer(); vkOffsets = offset*sizeof(Vertex); // Сколько будем рисовать в этом буфере size_t willDraw = std::min(Counter, Buffers[bufferIndex].getSize() / sizeof(Vertex) - offset); vkCmdBindVertexBuffers(renderCmd, 0, 1, &buff, &vkOffsets); vkCmdDraw(renderCmd, willDraw, 1, 0, 0); // Смещаемся Counter -= willDraw; Offset += willDraw; } } // Перед тем как начнём отрисовку // Чтобы загрузить всё на устройство void beforeStartDraw() { if(LastData) { Buffers.back().unMapMemory(); LastData = nullptr; } } void beforeRenderUpdate() { // Реалоцировать один большой буфер //Buffers.clear(); Offset = 0; Counter = 0; } }; class FontAtlas : public AtlasImage { public: struct GlyphInfo { uint16_t TexId; uint16_t IsValid = false; int16_t Width, Height; int16_t PosX, PosY; }; protected: struct Face { FT_Face Obj = nullptr; ByteBuffer Data; ~Face() { if(Obj) FT_Done_Face(Obj); } }; FT_Library FontLibrary = nullptr; std::list FontList; std::map CharToInfo; size_t LastUpdate = 0; void clearInvalidGlyphs(); public: FontAtlas(Vulkan *instace); virtual ~FontAtlas(); FontAtlas(const FontAtlas&) = delete; FontAtlas(FontAtlas&&) = delete; FontAtlas& operator=(const FontAtlas&) = delete; FontAtlas& operator=(FontAtlas&&) = delete; // Загружает шрифт в конец списка void pushFont(const std::variant &file); // Переписывает список шрифтов void setFontList(const std::vector> &fonts); // Получить или сгенерировать идентификатор для символа GlyphInfo getGlyph(UChar wc, uint16_t size); // Время последнего изменения данных size_t getLastUpdate() { return LastUpdate; } }; class PipelineVF : public Pipeline { std::string PathVertex, PathFragment; std::shared_ptr ShaderVertex, ShaderFragment; protected: virtual void init(Vulkan *instance) override; public: PipelineVF(std::shared_ptr layout, const std::string &vertex, const std::string &fragment); virtual ~PipelineVF(); }; class PipelineVGF : public Pipeline { std::string PathVertex, PathGeometry, PathFragment; std::shared_ptr ShaderVertex, ShaderGeometry, ShaderFragment; protected: virtual void init(Vulkan *instance) override; public: PipelineVGF(std::shared_ptr layout, const std::string &vertex, const std::string &geometry, const std::string &fragment); virtual ~PipelineVGF(); }; } /* namespace TOS::Navie::VK */