Фиг знает какие были изменения
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -849,49 +849,49 @@ VulkanRenderSession::VulkanRenderSession(Vulkan *vkInst, IServerSession *serverS
|
||||
&DescriptorPool));
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<VkDescriptorSetLayoutBinding> 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<VkDescriptorSetLayoutBinding> 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<VkDescriptorSetLayoutBinding> 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);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Common/Abstract.hpp"
|
||||
#include <Client/Vulkan/Vulkan.hpp>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -14,16 +15,20 @@
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#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 <execution>
|
||||
#include <MaxRectsBinPack.h>
|
||||
|
||||
/*
|
||||
У движка есть один текстурный атлас 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<std::tuple<ResourceId, Transformations>> Depends;
|
||||
|
||||
// Те кто использовали модель как зависимость в ней отметятся
|
||||
std::vector<ResourceId> UpUse;
|
||||
// При изменении/удалении модели убрать метки с зависимостей
|
||||
std::vector<ResourceId> DownUse;
|
||||
// Для постройки зависимостей
|
||||
bool Ready = false;
|
||||
};
|
||||
|
||||
public:
|
||||
// Предкомпилирует модель
|
||||
Model getModel(ResourceId id) {
|
||||
@@ -260,6 +253,19 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
struct ModelObject : public Model {
|
||||
// Зависимости, их трансформации (здесь может повторятся одна и таже модель)
|
||||
// и перезаписи идентификаторов текстур
|
||||
std::vector<std::tuple<ResourceId, Transformations>> Depends;
|
||||
|
||||
// Те кто использовали модель как зависимость в ней отметятся
|
||||
std::vector<ResourceId> UpUse;
|
||||
// При изменении/удалении модели убрать метки с зависимостей
|
||||
std::vector<ResourceId> DownUse;
|
||||
// Для постройки зависимостей
|
||||
bool Ready = false;
|
||||
};
|
||||
|
||||
Logger LOG = "Client>ModelProvider";
|
||||
// Таблица моделей
|
||||
std::unordered_map<ResourceId, ModelObject> 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<InfoSubTexture> 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<VkDescriptorSetLayoutBinding> 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<VkDescriptorSet> 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<AssetsTexture> onTexturesChanges(std::vector<std::tuple<AssetsTexture, Resource>> newOrChanged, std::vector<AssetsTexture> lost) {
|
||||
std::vector<AssetsTexture> 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<png::rgba_pixel> 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<glm::i8vec4> RGBA;
|
||||
|
||||
// Идентификатор текстуры в атласе
|
||||
uint16_t InAtlasId = uint16_t(-1);
|
||||
};
|
||||
|
||||
// Текстуры, загруженные с файлов
|
||||
std::unordered_map<AssetsTexture, TextureEntry> Textures;
|
||||
|
||||
struct TextureFromPipeline {
|
||||
|
||||
};
|
||||
|
||||
std::unordered_map<TexturePipeline, TextureFromPipeline> Pipelines;
|
||||
|
||||
struct AtlasTextureEntry {
|
||||
uint16_t PosX, PosY, PosZ, Width, Height;
|
||||
|
||||
};
|
||||
|
||||
std::bitset<1 << 16> AtlasTextureUnusedId;
|
||||
std::unordered_map<uint16_t, AtlasTextureEntry> AtlasTextureInfo;
|
||||
|
||||
std::vector<AssetsTexture> 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<VkImageUsageFlags>(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<uint32_t*> Layers;
|
||||
std::vector<VkSubresourceLayout> Layouts;
|
||||
std::vector<rbp::MaxRectsBinPack> 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<VkAtlasInfo> Atlases;
|
||||
int ActiveAtlas = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Хранит информацию о моделях при различных состояниях нод
|
||||
*/
|
||||
class NodestateProvider {
|
||||
public:
|
||||
struct Model {
|
||||
// В вершинах текущей модели TexId ссылается на локальный текстурный ключ
|
||||
// 0 -> default_texture -> luavox:grass.png
|
||||
std::vector<std::string> TextureKeys;
|
||||
// Привязка локальных ключей к глобальным
|
||||
std::unordered_map<std::string, TexturePipeline> TextureMap;
|
||||
// Вершины со всеми применёнными трансформациями, с CullFace
|
||||
std::unordered_map<EnumFace, std::vector<NodeVertexStatic>> 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<std::string> Variable;
|
||||
int Variations = 0;
|
||||
};
|
||||
|
||||
// Выдаёт модели в зависимости от состояний
|
||||
// statesInfo - Описание состояний ноды
|
||||
// states - Текущие значения состояний ноды
|
||||
std::vector<Model> getModelsForNode(AssetsNodestate id, const std::vector<StateInfo>& statesInfo, const std::unordered_map<std::string, int>& states) {
|
||||
std::vector<std::vector<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>>> getModelsForNode(AssetsNodestate id, const std::vector<NodeStateInfo>& statesInfo, const std::unordered_map<std::string, int32_t>& states) {
|
||||
auto iterNodestate = Nodestates.find(id);
|
||||
if(iterNodestate == Nodestates.end())
|
||||
return {};
|
||||
|
||||
iterNodestate->second;
|
||||
std::vector<uint16_t> routes = iterNodestate->second.getModelsForState(statesInfo, states);
|
||||
std::vector<std::vector<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>>> result;
|
||||
|
||||
std::unordered_map<TexturePipeline, uint16_t> pipelineResolveCache;
|
||||
|
||||
for(uint16_t routeId : routes) {
|
||||
std::vector<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>> routeModels;
|
||||
const auto& route = iterNodestate->second.Routes[routeId];
|
||||
for(const auto& [w, m] : route.second) {
|
||||
if(const PreparedNodeState::Model* ptr = std::get_if<PreparedNodeState::Model>(&m)) {
|
||||
ModelProvider::Model model = MP.getModel(ptr->Id);
|
||||
Transformations trf(ptr->Transforms);
|
||||
std::unordered_map<EnumFace, std::vector<NodeVertexStatic>> 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<int32_t>(v.UV.x * (1 << 16), 0, (1 << 16));
|
||||
vert.TV = std::clamp<int32_t>(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<AssetsNodestate, PreparedNodeState> 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, Воксельная карта освещения
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
#include "TOSLib.hpp"
|
||||
#include "boost/json/array.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <glm/ext.hpp>
|
||||
#include <memory>
|
||||
#include <sol/forward.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -602,6 +604,12 @@ struct Transformations {
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeStateInfo {
|
||||
std::string Name;
|
||||
std::vector<std::string> Variable;
|
||||
int Variations = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Хранит распаршенное определение состояний нод.
|
||||
Не привязано ни к какому окружению.
|
||||
@@ -670,6 +678,172 @@ struct PreparedNodeState {
|
||||
return HasVariability;
|
||||
}
|
||||
|
||||
// Возвращает идентификаторы routes прошедшии по состояниям
|
||||
std::vector<uint16_t> getModelsForState(const std::vector<NodeStateInfo>& statesInfo, const std::unordered_map<std::string, int32_t>& states) {
|
||||
std::unordered_map<std::string, int32_t> values;
|
||||
std::vector<uint16_t> upUse;
|
||||
|
||||
// Проверить какие переменные упоминаются для составления таблицы быстрых значений (без обозначения <имя состояния>:<вариант состояния>)
|
||||
{
|
||||
std::vector<std::string> variables;
|
||||
std::move_only_function<void(uint16_t nodeId)> 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::Var>(&node.v)) {
|
||||
variables.push_back(ptr->name);
|
||||
} else if(Node::Unary* ptr = std::get_if<Node::Unary>(&node.v)) {
|
||||
upUse.push_back(nodeId);
|
||||
lambda(ptr->rhs);
|
||||
upUse.pop_back();
|
||||
} else if(Node::Binary* ptr = std::get_if<Node::Binary>(&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<int32_t(uint16_t nodeId)> 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::Num>(&node.v)) {
|
||||
result = ptr->v;
|
||||
} else if(Node::Var* ptr = std::get_if<Node::Var>(&node.v)) {
|
||||
result = values.at(ptr->name);
|
||||
} else if(Node::Unary* ptr = std::get_if<Node::Unary>(&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::Binary>(&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<uint16_t> 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<LV::Hash_t> {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<LV::TexturePipeline> {
|
||||
std::size_t operator()(const LV::TexturePipeline& tp) const noexcept {
|
||||
size_t seed = 0;
|
||||
|
||||
for (const auto& tex : tp.BinTextures)
|
||||
seed ^= std::hash<LV::AssetsTexture>{}(tex) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
|
||||
std::string_view sv(reinterpret_cast<const char*>(tp.Pipeline.data()), tp.Pipeline.size());
|
||||
seed ^= std::hash<std::string_view>{}(sv) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user