From b1bc2c8e74b42196a36e87de3ae08c98f4c9f7b9 Mon Sep 17 00:00:00 2001 From: DrSocalkwe3n Date: Wed, 31 Dec 2025 15:02:09 +0600 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=B3=20=D0=B7=D0=BD=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BA=D0=B0=D0=BA=D0=B8=D0=B5=20=D0=B1=D1=8B=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 8 + Src/Client/Vulkan/Vulkan.cpp | 2 +- Src/Client/Vulkan/VulkanRenderSession.cpp | 82 +-- Src/Client/Vulkan/VulkanRenderSession.hpp | 602 ++++++++++++++++++++-- Src/Common/Abstract.hpp | 188 +++++++ assets/shaders/chunk/node.vert.bin | Bin 2600 -> 2600 bytes 6 files changed, 804 insertions(+), 78 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 33fce95..be0a9f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,14 @@ FetchContent_Declare(sqlite3 GIT_REPOSITORY https://github.com/sjinks/sqlite3-cm FetchContent_MakeAvailable(sqlite3) target_link_libraries(luavox_common INTERFACE SQLite::SQLite3) +FetchContent_Declare( + RectangleBinPack + GIT_REPOSITORY https://github.com/juj/RectangleBinPack.git + GIT_TAG 83e7e1132d93777e3732dfaae26b0f3703be2036 +) +FetchContent_MakeAvailable(RectangleBinPack) +target_link_libraries(luavox_common INTERFACE RectangleBinPack) + # Static Assets find_package(Python3 REQUIRED) set(ASSETS_DIR "${PROJECT_SOURCE_DIR}/assets") diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index fefc70f..a62ac14 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -286,7 +286,7 @@ void Vulkan::run() err = vkAcquireNextImageKHR(Graphics.Device, Graphics.Swapchain, 1000000000ULL/20, SemaphoreImageAcquired[semNext], (VkFence) 0, &Graphics.DrawBufferCurrent); GlobalTime gTime = glfwGetTime(); - if (err == VK_ERROR_OUT_OF_DATE_KHR) + if(err == VK_ERROR_OUT_OF_DATE_KHR) { if(Game.RSession) Game.RSession->pushStage(EnumRenderStage::WorldUpdate); diff --git a/Src/Client/Vulkan/VulkanRenderSession.cpp b/Src/Client/Vulkan/VulkanRenderSession.cpp index 9e83680..fe3301d 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.cpp +++ b/Src/Client/Vulkan/VulkanRenderSession.cpp @@ -849,49 +849,49 @@ VulkanRenderSession::VulkanRenderSession(Vulkan *vkInst, IServerSession *serverS &DescriptorPool)); } - { - std::vector shaderLayoutBindings = - { - { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr - }, { - .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr - } - }; + // { + // std::vector shaderLayoutBindings = + // { + // { + // .binding = 0, + // .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + // .descriptorCount = 1, + // .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + // .pImmutableSamplers = nullptr + // }, { + // .binding = 1, + // .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + // .descriptorCount = 1, + // .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + // .pImmutableSamplers = nullptr + // } + // }; - const VkDescriptorSetLayoutCreateInfo descriptorLayout = - { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .bindingCount = (uint32_t) shaderLayoutBindings.size(), - .pBindings = shaderLayoutBindings.data() - }; + // const VkDescriptorSetLayoutCreateInfo descriptorLayout = + // { + // .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + // .pNext = nullptr, + // .flags = 0, + // .bindingCount = (uint32_t) shaderLayoutBindings.size(), + // .pBindings = shaderLayoutBindings.data() + // }; - vkAssert(!vkCreateDescriptorSetLayout( - VkInst->Graphics.Device, &descriptorLayout, nullptr, &MainAtlasDescLayout)); - } + // vkAssert(!vkCreateDescriptorSetLayout( + // VkInst->Graphics.Device, &descriptorLayout, nullptr, &MainAtlasDescLayout)); + // } - { - VkDescriptorSetAllocateInfo ciAllocInfo = - { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = DescriptorPool, - .descriptorSetCount = 1, - .pSetLayouts = &MainAtlasDescLayout - }; + // { + // VkDescriptorSetAllocateInfo ciAllocInfo = + // { + // .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + // .pNext = nullptr, + // .descriptorPool = DescriptorPool, + // .descriptorSetCount = 1, + // .pSetLayouts = &MainAtlasDescLayout + // }; - vkAssert(!vkAllocateDescriptorSets(VkInst->Graphics.Device, &ciAllocInfo, &MainAtlasDescriptor)); - } + // vkAssert(!vkAllocateDescriptorSets(VkInst->Graphics.Device, &ciAllocInfo, &MainAtlasDescriptor)); + // } { std::vector shaderLayoutBindings = @@ -1434,8 +1434,8 @@ VulkanRenderSession::~VulkanRenderSession() { if(MainAtlas_LightMap_PipelineLayout) vkDestroyPipelineLayout(VkInst->Graphics.Device, MainAtlas_LightMap_PipelineLayout, nullptr); - if(MainAtlasDescLayout) - vkDestroyDescriptorSetLayout(VkInst->Graphics.Device, MainAtlasDescLayout, nullptr); + // if(MainAtlasDescLayout) + // vkDestroyDescriptorSetLayout(VkInst->Graphics.Device, MainAtlasDescLayout, nullptr); if(VoxelLightMapDescLayout) vkDestroyDescriptorSetLayout(VkInst->Graphics.Device, VoxelLightMapDescLayout, nullptr); diff --git a/Src/Client/Vulkan/VulkanRenderSession.hpp b/Src/Client/Vulkan/VulkanRenderSession.hpp index 080f40f..790772a 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.hpp +++ b/Src/Client/Vulkan/VulkanRenderSession.hpp @@ -4,6 +4,7 @@ #include "Common/Abstract.hpp" #include #include +#include #include #include #include @@ -14,16 +15,20 @@ #include #include #include +#include #include #include #include "Abstract.hpp" #include "TOSLib.hpp" #include "VertexPool.hpp" +#include "assets.hpp" #include "glm/common.hpp" #include "glm/fwd.hpp" #include "../FrustumCull.h" #include "glm/geometric.hpp" +#include "png++/image.hpp" #include +#include /* У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур @@ -57,6 +62,7 @@ static_assert(sizeof(WorldPCO) == 128); Хранит модели и предоставляет их конечные варианты */ class ModelProvider { +public: struct Model { // В вершинах текущей модели TexId ссылается на локальный текстурный ключ // 0 -> default_texture -> luavox:grass.png @@ -69,19 +75,6 @@ class ModelProvider { bool UniqueTextures = false; }; - struct ModelObject : public Model { - // Зависимости, их трансформации (здесь может повторятся одна и таже модель) - // и перезаписи идентификаторов текстур - std::vector> Depends; - - // Те кто использовали модель как зависимость в ней отметятся - std::vector UpUse; - // При изменении/удалении модели убрать метки с зависимостей - std::vector DownUse; - // Для постройки зависимостей - bool Ready = false; - }; - public: // Предкомпилирует модель Model getModel(ResourceId id) { @@ -260,6 +253,19 @@ public: } private: + struct ModelObject : public Model { + // Зависимости, их трансформации (здесь может повторятся одна и таже модель) + // и перезаписи идентификаторов текстур + std::vector> Depends; + + // Те кто использовали модель как зависимость в ней отметятся + std::vector UpUse; + // При изменении/удалении модели убрать метки с зависимостей + std::vector DownUse; + // Для постройки зависимостей + bool Ready = false; + }; + Logger LOG = "Client>ModelProvider"; // Таблица моделей std::unordered_map Models; @@ -364,24 +370,504 @@ private: } }; +/** + Обработчик текстурных атласов для моделей + + Функция выделения места в полном объёме. + + Перед отрисовкой кадра выставить команды ожидания завершения передачи данных на GPU, до вызова стадии FRAGMENT_STAGE + + + Одновременно может проходить два рендера (двойные копии ресурсов) + Когда одна копия освободилась, в неё начинается запись + + Два атласа, постоянно редактируются меж кадрами + 2D_ARRAY и одну текстуру на стороне хоста для обмена информацией + Ужатие hd текстур + + Хранить все текстуры в оперативке +*/ +class TextureProvider { + // Хедер для атласа перед описанием текстур + struct alignas(16) UniformInfo { + uint32_t + // Количество текстур + SubsCount, + // Счётчик времени с разрешением 8 бит в секунду + Counter, + // Размер атласа + Size; + + // Дальше в шейдере массив на описания текстур + // std::vector SubsInfo; + }; + + // Описание текстуры на стороне шейдера + struct alignas(16) InfoSubTexture { + uint32_t isExist = 0; + uint32_t + // Точная позиция в атласе + PosX = 0, PosY = 0, PosZ = 0, + // Размер текстуры в атласе + Width = 0, Height = 0; + + struct { + uint16_t Enabled : 1 = 0, Frames : 15 = 0; + uint16_t TimePerFrame = 0; + } Animation; + }; + +public: + TextureProvider(Vulkan* inst, VkDescriptorPool descPool) + : Inst(inst), DescPool(descPool) + { + { + const VkSamplerCreateInfo ciSampler = + { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 1, + .compareEnable = 0, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + .unnormalizedCoordinates = VK_FALSE + }; + vkAssert(!vkCreateSampler(inst->Graphics.Device, &ciSampler, nullptr, &Sampler)); + } + + { + std::vector shaderLayoutBindings = + { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr + }, { + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr + } + }; + + const VkDescriptorSetLayoutCreateInfo descriptorLayout = + { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .bindingCount = (uint32_t) shaderLayoutBindings.size(), + .pBindings = shaderLayoutBindings.data() + }; + + vkAssert(!vkCreateDescriptorSetLayout( + Inst->Graphics.Device, &descriptorLayout, nullptr, &DescLayout)); + } + + { + Atlases.resize(BackupAtlasCount); + + VkDescriptorSetAllocateInfo ciAllocInfo = + { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = descPool, + .descriptorSetCount = (uint32_t) Atlases.size(), + .pSetLayouts = &DescLayout + }; + + std::vector descriptors; + descriptors.resize(Atlases.size()); + vkAssert(!vkAllocateDescriptorSets(inst->Graphics.Device, &ciAllocInfo, descriptors.data())); + + for(auto& atlas : Atlases) { + atlas.recreate(Inst, true); + atlas.Descriptor = descriptors.back(); + descriptors.pop_back(); + } + } + + { + VkSemaphoreCreateInfo semaphoreCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = nullptr, + .flags = 0 + }; + + vkAssert(!vkCreateSemaphore(Inst->Graphics.Device, &semaphoreCreateInfo, nullptr, &SendChanges)); + } + + { + const VkCommandBufferAllocateInfo infoCmd = + { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = Inst->Graphics.Pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1 + }; + + vkAssert(!vkAllocateCommandBuffers(Inst->Graphics.Device, &infoCmd, &CMD)); + } + + Cache.recreate(Inst, false); + + AtlasTextureUnusedId.all(); + } + + ~TextureProvider() { + if(DescLayout) + vkDestroyDescriptorSetLayout(Inst->Graphics.Device, DescLayout, nullptr); + + if(Sampler) { + vkDestroySampler(Inst->Graphics.Device, Sampler, nullptr); + Sampler = nullptr; + } + + for(auto& atlas : Atlases) { + atlas.destroy(Inst); + } + + Atlases.clear(); + + Cache.destroy(Inst); + Cache.unMap(Inst); + + if(SendChanges) { + vkDestroySemaphore(Inst->Graphics.Device, SendChanges, nullptr); + SendChanges = nullptr; + } + + if(CMD) { + vkFreeCommandBuffers(Inst->Graphics.Device, Inst->Graphics.Pool, 1, &CMD); + CMD = nullptr; + } + } + + uint16_t getTextureId(const TexturePipeline& pipe) { + return 0; + } + + // Устанавливает новый размер единицы в массиве текстур атласа + enum class EnumAtlasSize { + _2048 = 2048, _4096 = 4096, _8192 = 8192, _16_384 = 16'384 + }; + void setAtlasSize(EnumAtlasSize size) { + ReferenceSize = size; + } + + // Максимальный размер выделенный под атласы в памяти устройства + void setDeviceMemorySize(size_t size) { + std::unreachable(); + } + + // Применяет изменения, возвращая все затронутые модели + std::vector onTexturesChanges(std::vector> newOrChanged, std::vector lost) { + std::vector result; + + for(const auto& [key, res] : newOrChanged) { + result.push_back(key); + ChangedOrAdded.push_back(key); + + TextureEntry entry; + iResource sres((const uint8_t*) res.data(), res.size()); + iBinaryStream stream = sres.makeStream(); + png::image img(stream.Stream); + entry.Width = img.get_width(); + entry.Height = img.get_height(); + entry.RGBA.resize(4*entry.Width*entry.Height); + + for(int i = 0; i < entry.Height; i++) { + std::copy( + ((const uint32_t*) &img.get_pixbuf().operator [](i)[0]), + ((const uint32_t*) &img.get_pixbuf().operator [](i)[0])+entry.Width, + ((uint32_t*) entry.RGBA.data())+entry.Width*(false ? entry.Height-i-1 : i) + ); + } + + Textures[key] = std::move(entry); + } + + for(AssetsTexture key : lost) { + result.push_back(key); + Lost.push_back(key); + } + + { + std::sort(result.begin(), result.end()); + auto eraseIter = std::unique(result.begin(), result.end()); + result.erase(eraseIter, result.end()); + } + + { + std::sort(ChangedOrAdded.begin(), ChangedOrAdded.end()); + auto eraseIter = std::unique(ChangedOrAdded.begin(), ChangedOrAdded.end()); + ChangedOrAdded.erase(eraseIter, ChangedOrAdded.end()); + } + + { + std::sort(Lost.begin(), Lost.end()); + auto eraseIter = std::unique(Lost.begin(), Lost.end()); + Lost.erase(eraseIter, Lost.end()); + } + + return result; + } + + void update() { + // Подготовить обновления атласа + // Если предыдущий освободился, то записать изменения в него + + // Держать на стороне хоста полную версию атласа и все изменения писать туда + // Когерентная память сама разберётся что отсылать на устройство + // Синхронизировать всё из внутреннего буфера в атлас + // При пересоздании хостового буфера, скопировать всё из старого. + + // Оптимизации копирования при указании конкретных изменённых слоёв? + + + } + + VkDescriptorSet getDescriptor() { + return Atlases[ActiveAtlas].Descriptor; + } + + void pushFrame() { + for(auto& atlas : Atlases) + if(atlas.NotUsedFrames < 100) + atlas.NotUsedFrames++; + + Atlases[ActiveAtlas].NotUsedFrames = 0; + + // Если есть новые текстуры или они поменялись + // + } + +private: + Vulkan* Inst = nullptr; + VkDescriptorPool DescPool = VK_NULL_HANDLE; + VkDescriptorSetLayout DescLayout = VK_NULL_HANDLE; + // Для всех атласов + VkSampler Sampler = VK_NULL_HANDLE; + // Ожидание завершения работы с хостовым буфером + VkSemaphore SendChanges = VK_NULL_HANDLE; + // + VkCommandBuffer CMD = VK_NULL_HANDLE; + + // Размер, которому должны соответствовать все атласы + EnumAtlasSize ReferenceSize = EnumAtlasSize::_2048; + + struct TextureEntry { + uint16_t Width, Height; + std::vector RGBA; + + // Идентификатор текстуры в атласе + uint16_t InAtlasId = uint16_t(-1); + }; + + // Текстуры, загруженные с файлов + std::unordered_map Textures; + + struct TextureFromPipeline { + + }; + + std::unordered_map Pipelines; + + struct AtlasTextureEntry { + uint16_t PosX, PosY, PosZ, Width, Height; + + }; + + std::bitset<1 << 16> AtlasTextureUnusedId; + std::unordered_map AtlasTextureInfo; + + std::vector ChangedOrAdded, Lost; + + struct VkAtlasInfo { + VkImage Image = VK_NULL_HANDLE; + VkImageLayout ImageLayout = VK_IMAGE_LAYOUT_MAX_ENUM; + + VkDeviceMemory Memory = VK_NULL_HANDLE; + VkImageView View = VK_NULL_HANDLE; + + VkDescriptorSet Descriptor; + EnumAtlasSize Size = EnumAtlasSize::_2048; + uint16_t Depth = 1; + + // Сколько кадров уже не используется атлас + int NotUsedFrames = 0; + + void destroy(Vulkan* inst) { + if(View) { + vkDestroyImageView(inst->Graphics.Device, View, nullptr); + View = nullptr; + } + + if(Image) { + vkDestroyImage(inst->Graphics.Device, Image, nullptr); + Image = nullptr; + } + + if(Memory) { + vkFreeMemory(inst->Graphics.Device, Memory, nullptr); + Memory = nullptr; + } + } + + void recreate(Vulkan* inst, bool deviceLocal) { + // Уничтожаем то, что не понадобится + if(View) { + vkDestroyImageView(inst->Graphics.Device, View, nullptr); + View = nullptr; + } + + if(Image) { + vkDestroyImage(inst->Graphics.Device, Image, nullptr); + Image = nullptr; + } + + if(Memory) { + vkFreeMemory(inst->Graphics.Device, Memory, nullptr); + Memory = nullptr; + } + + // Создаём атлас + uint32_t size = uint32_t(Size); + + VkImageCreateInfo infoImageCreate = + { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = VK_FORMAT_B8G8R8A8_UNORM, + .extent = { size, size, 1 }, + .mipLevels = 1, + .arrayLayers = Depth, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_MAX_ENUM, + .usage = + static_cast(deviceLocal + ? VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT + : VK_IMAGE_USAGE_TRANSFER_SRC_BIT), + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = 0, + .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED + }; + + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(inst->Graphics.PhysicalDevice, infoImageCreate.format, &props); + + if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) + infoImageCreate.tiling = VK_IMAGE_TILING_OPTIMAL; + else if (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) + infoImageCreate.tiling = VK_IMAGE_TILING_LINEAR; + else + vkAssert(!"No support for B8G8R8A8_UNORM as texture image format"); + + vkAssert(!vkCreateImage(inst->Graphics.Device, &infoImageCreate, nullptr, &Image)); + + // Выделяем память + VkMemoryRequirements memoryReqs; + vkGetImageMemoryRequirements(inst->Graphics.Device, Image, &memoryReqs); + + VkMemoryAllocateInfo memoryAlloc + { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = nullptr, + .allocationSize = memoryReqs.size, + .memoryTypeIndex = inst->memoryTypeFromProperties(memoryReqs.memoryTypeBits, + deviceLocal + ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + : VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + ) + }; + + vkAssert(!vkAllocateMemory(inst->Graphics.Device, &memoryAlloc, nullptr, &Memory)); + vkAssert(!vkBindImageMemory(inst->Graphics.Device, Image, Memory, 0)); + + // Порядок пикселей и привязка к картинке + VkImageViewCreateInfo ciView = + { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = Image, + .viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY, + .format = infoImageCreate.format, + .components = + { + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_A + }, + .subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } + }; + + vkAssert(!vkCreateImageView(inst->Graphics.Device, &ciView, nullptr, &View)); + } + }; + + struct HostCache : public VkAtlasInfo { + std::vector Layers; + std::vector Layouts; + std::vector Packs; + + void map(Vulkan* inst) { + Layers.resize(Depth); + Layouts.resize(Depth); + + for(uint32_t layer = 0; layer < Depth; layer++) { + const VkImageSubresource memorySubres = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = layer, }; + vkGetImageSubresourceLayout(inst->Graphics.Device, Image, &memorySubres, &Layouts[layer]); + + vkAssert(!vkMapMemory(inst->Graphics.Device, Memory, Layouts[layer].offset, Layouts[layer].size, 0, (void**) &Layers[layer])); + } + } + + void unMap(Vulkan* inst) { + vkUnmapMemory(inst->Graphics.Device, Memory); + + Layers.clear(); + Layouts.clear(); + } + }; + + HostCache Cache; + + static constexpr size_t BackupAtlasCount = 2; + + // Атласы, используемые в кадре. + // Изменения пишутся в не используемый в данный момент атлас + // и изменённый атлас становится активным. Новые изменения + // можно писать по прошествии нескольких кадров. + std::vector Atlases; + int ActiveAtlas = 0; +}; + /* Хранит информацию о моделях при различных состояниях нод */ class NodestateProvider { public: - struct Model { - // В вершинах текущей модели TexId ссылается на локальный текстурный ключ - // 0 -> default_texture -> luavox:grass.png - std::vector TextureKeys; - // Привязка локальных ключей к глобальным - std::unordered_map TextureMap; - // Вершины со всеми применёнными трансформациями, с CullFace - std::unordered_map> Vertecies; - }; - -public: - NodestateProvider(ModelProvider& mp) - : MP(mp) + NodestateProvider(ModelProvider& mp, TextureProvider& tp) + : MP(mp), TP(tp) {} // Применяет изменения, возвращает изменённые описания состояний @@ -428,26 +914,70 @@ public: return result; } - struct StateInfo { - std::string Name; - std::vector Variable; - int Variations = 0; - }; - // Выдаёт модели в зависимости от состояний // statesInfo - Описание состояний ноды // states - Текущие значения состояний ноды - std::vector getModelsForNode(AssetsNodestate id, const std::vector& statesInfo, const std::unordered_map& states) { + std::vector>>>> getModelsForNode(AssetsNodestate id, const std::vector& statesInfo, const std::unordered_map& states) { auto iterNodestate = Nodestates.find(id); if(iterNodestate == Nodestates.end()) return {}; - iterNodestate->second; + std::vector routes = iterNodestate->second.getModelsForState(statesInfo, states); + std::vector>>>> result; + + std::unordered_map pipelineResolveCache; + + for(uint16_t routeId : routes) { + std::vector>>> routeModels; + const auto& route = iterNodestate->second.Routes[routeId]; + for(const auto& [w, m] : route.second) { + if(const PreparedNodeState::Model* ptr = std::get_if(&m)) { + ModelProvider::Model model = MP.getModel(ptr->Id); + Transformations trf(ptr->Transforms); + std::unordered_map> out; + + for(auto& [l, r] : model.Vertecies) { + trf.apply(r); + + // Позиция -224 ~ 288; 64 позиций в одной ноде, 7.5 метров в ряд + for(const Vertex& v : r) { + NodeVertexStatic vert; + + vert.FX = (v.Pos.x+0.5)*64+224; + vert.FY = (v.Pos.y+0.5)*64+224; + vert.FZ = (v.Pos.z+0.5)*64+224; + + vert.TU = std::clamp(v.UV.x * (1 << 16), 0, (1 << 16)); + vert.TV = std::clamp(v.UV.y * (1 << 16), 0, (1 << 16)); + + const TexturePipeline& pipe = model.TextureMap[model.TextureKeys[v.TexId]]; + if(auto iterPipe = pipelineResolveCache.find(pipe); iterPipe != pipelineResolveCache.end()) { + vert.Tex = iterPipe->second; + } else { + vert.Tex = TP.getTextureId(pipe); + pipelineResolveCache[pipe] = vert.Tex; + } + + out[l].push_back(vert); + } + } + + /// TODO: uvlock + + routeModels.emplace_back(w, std::move(out)); + } + } + + result.push_back(std::move(routeModels)); + } + + return result; } private: Logger LOG = "Client>NodestateProvider"; ModelProvider& MP; + TextureProvider& TP; std::unordered_map Nodestates; }; @@ -676,8 +1206,8 @@ class VulkanRenderSession : public IRenderSession { .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Данные к атласу */ - VkDescriptorSetLayout MainAtlasDescLayout = VK_NULL_HANDLE; - VkDescriptorSet MainAtlasDescriptor = VK_NULL_HANDLE; + // VkDescriptorSetLayout MainAtlasDescLayout = VK_NULL_HANDLE; + // VkDescriptorSet MainAtlasDescriptor = VK_NULL_HANDLE; /* .binding = 2, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Воксельная карта освещения diff --git a/Src/Common/Abstract.hpp b/Src/Common/Abstract.hpp index fde36b6..ce2f8e5 100644 --- a/Src/Common/Abstract.hpp +++ b/Src/Common/Abstract.hpp @@ -2,11 +2,13 @@ #include "TOSLib.hpp" #include "boost/json/array.hpp" +#include #include #include #include #include #include +#include #include #include #include @@ -602,6 +604,12 @@ struct Transformations { } }; +struct NodeStateInfo { + std::string Name; + std::vector Variable; + int Variations = 0; +}; + /* Хранит распаршенное определение состояний нод. Не привязано ни к какому окружению. @@ -670,6 +678,172 @@ struct PreparedNodeState { return HasVariability; } + // Возвращает идентификаторы routes прошедшии по состояниям + std::vector getModelsForState(const std::vector& statesInfo, const std::unordered_map& states) { + std::unordered_map values; + std::vector upUse; + + // Проверить какие переменные упоминаются для составления таблицы быстрых значений (без обозначения <имя состояния>:<вариант состояния>) + { + std::vector variables; + std::move_only_function lambda; + + lambda = [&](uint16_t nodeId) { + Node& node = Nodes.at(nodeId); + + if(std::find(upUse.begin(), upUse.end(), nodeId) != upUse.end()) { + MAKE_ERROR("Циклическая зависимость нод"); + } + + if(Node::Var* ptr = std::get_if(&node.v)) { + variables.push_back(ptr->name); + } else if(Node::Unary* ptr = std::get_if(&node.v)) { + upUse.push_back(nodeId); + lambda(ptr->rhs); + upUse.pop_back(); + } else if(Node::Binary* ptr = std::get_if(&node.v)) { + upUse.push_back(nodeId); + lambda(ptr->lhs); + lambda(ptr->rhs); + upUse.pop_back(); + } + }; + + std::sort(variables.begin(), variables.end()); + auto eraseIter = std::unique(variables.begin(), variables.end()); + variables.erase(eraseIter, variables.end()); + + bool ok = false; + + for(const std::string_view key : variables) { + if(size_t pos = key.find(':'); pos != std::string::npos) { + std::string_view state, value; + state = key.substr(0, pos); + value = key.substr(pos+1); + + for(const NodeStateInfo& info : statesInfo) { + if(info.Name != state) + continue; + + for(size_t iter = 0; iter < info.Variable.size(); iter++) { + if(info.Variable[iter] == value) { + ok = true; + values[(const std::string) key] = iter; + break; + } + } + + break; + } + } else { + for(const NodeStateInfo& info : statesInfo) { + if(info.Name == key) { + ok = true; + values[(const std::string) key] = states.at((std::string) key); + break; + } + + for(size_t iter = 0; iter < info.Variable.size(); iter++) { + if(info.Variable[iter] == key) { + ok = true; + values[(const std::string) key] = iter; + break; + } + } + + if(ok) + break; + } + } + + if(!ok) + values[(const std::string) key] = 0; + } + } + + + std::move_only_function calcNode; + + calcNode = [&](uint16_t nodeId) -> int32_t { + if(std::find(upUse.begin(), upUse.end(), nodeId) != upUse.end()) { + MAKE_ERROR("Циклическая зависимость нод"); + } + + int32_t result; + Node& node = Nodes.at(nodeId); + + if(Node::Num* ptr = std::get_if(&node.v)) { + result = ptr->v; + } else if(Node::Var* ptr = std::get_if(&node.v)) { + result = values.at(ptr->name); + } else if(Node::Unary* ptr = std::get_if(&node.v)) { + int32_t rhs; + + upUse.push_back(nodeId); + rhs = calcNode(ptr->rhs); + upUse.pop_back(); + + if(ptr->op == Op::Not) { + result = !rhs; + } else if(ptr->op == Op::Pos) { + result = +rhs; + } else if(ptr->op == Op::Neg) { + result = -rhs; + } else + MAKE_ERROR("Ошибка в данных"); + } else if(Node::Binary* ptr = std::get_if(&node.v)) { + int32_t lhs, rhs; + + upUse.push_back(nodeId); + lhs = calcNode(ptr->lhs); + rhs = calcNode(ptr->rhs); + upUse.pop_back(); + + if(ptr->op == Op::Add) { + result = lhs+rhs; + } else if(ptr->op == Op::Sub) { + result = lhs-rhs; + } else if(ptr->op == Op::Mul) { + result = lhs*rhs; + } else if(ptr->op == Op::Div) { + result = lhs/rhs; + } else if(ptr->op == Op::Mod) { + result = lhs%rhs; + } else if(ptr->op == Op::And) { + result = lhs && rhs; + } else if(ptr->op == Op::Or) { + result = lhs || rhs; + } else if(ptr->op == Op::LT) { + result = lhs < rhs; + } else if(ptr->op == Op::LE) { + result = lhs <= rhs; + } else if(ptr->op == Op::GT) { + result = lhs > rhs; + } else if(ptr->op == Op::GE) { + result = lhs >= rhs; + } else if(ptr->op == Op::EQ) { + result = lhs == rhs; + } else if(ptr->op == Op::NE) { + result = lhs != rhs; + } else { + MAKE_ERROR("Ошибка в данных"); + } + } + + return result; + }; + + std::vector out; + + for(size_t iter = 0; iter < Routes.size(); iter++) { + if(calcNode(Routes[iter].first)) { + out.push_back(iter); + } + } + + return out; + } + private: bool HasVariability = false; @@ -851,4 +1025,18 @@ struct hash { } }; +template <> +struct hash { + std::size_t operator()(const LV::TexturePipeline& tp) const noexcept { + size_t seed = 0; + + for (const auto& tex : tp.BinTextures) + seed ^= std::hash{}(tex) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + + std::string_view sv(reinterpret_cast(tp.Pipeline.data()), tp.Pipeline.size()); + seed ^= std::hash{}(sv) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + + return seed; + } +}; } diff --git a/assets/shaders/chunk/node.vert.bin b/assets/shaders/chunk/node.vert.bin index c255cd65940b82be135ff4eacc8ccc1f0c7a6ffa..b78d2661b7ead08e9470464fcf1abb2f865fe94d 100644 GIT binary patch delta 30 kcmZ1>vO;7-K8v7}HUkR-8v_G_0uVDWBsgqtU|GQm0AH~M$N&HU delta 30 kcmZ1>vO;7-K8v8EHUkR-8v_G_0uVDWusd#UU|GQm09`T#ZvX%Q