Доработка пайплайн машины (требуется пересмотр технологии)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "Abstract.hpp"
|
||||
#include "Client/Vulkan/AtlasPipeline/TexturePipelineProgram.hpp"
|
||||
#include "Common/Net.hpp"
|
||||
#include "TOSLib.hpp"
|
||||
#include <boost/interprocess/file_mapping.hpp>
|
||||
@@ -6,6 +7,8 @@
|
||||
#include "boost/json.hpp"
|
||||
#include "sha2.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||
#include <boost/iostreams/copy.hpp>
|
||||
#include <boost/iostreams/filter/zlib.hpp>
|
||||
@@ -15,6 +18,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
|
||||
@@ -22,6 +26,50 @@ namespace LV {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, std::string_view defaultDomain) {
|
||||
PrecompiledTexturePipeline result;
|
||||
|
||||
std::string_view view(cmd);
|
||||
const size_t trimPos = view.find_first_not_of(" \t\r\n");
|
||||
if(trimPos == std::string_view::npos)
|
||||
MAKE_ERROR("Пустая текстурная команда");
|
||||
|
||||
view = view.substr(trimPos);
|
||||
|
||||
const bool isPipeline = view.size() >= 3
|
||||
&& view.compare(0, 3, "tex") == 0
|
||||
&& (view.size() == 3 || std::isspace(static_cast<unsigned char>(view[3])));
|
||||
|
||||
if(!isPipeline) {
|
||||
auto [domain, key] = parseDomainKey(std::string(view), defaultDomain);
|
||||
result.Assets.emplace_back(std::move(domain), std::move(key));
|
||||
return result;
|
||||
}
|
||||
|
||||
TexturePipelineProgram program;
|
||||
std::string err;
|
||||
if(!program.compile(std::string(view), &err)) {
|
||||
MAKE_ERROR("Ошибка разбора pipeline: " << err);
|
||||
}
|
||||
|
||||
result.IsSource = true;
|
||||
result.Pipeline.assign(reinterpret_cast<const char8_t*>(view.data()), view.size());
|
||||
|
||||
std::unordered_set<std::string> seen;
|
||||
for(const auto& patch : program.patches()) {
|
||||
auto [domain, key] = parseDomainKey(patch.Name, defaultDomain);
|
||||
std::string token;
|
||||
token.reserve(domain.size() + key.size() + 1);
|
||||
token.append(domain);
|
||||
token.push_back(':');
|
||||
token.append(key);
|
||||
if(seen.insert(token).second)
|
||||
result.Assets.emplace_back(std::move(domain), std::move(key));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
||||
std::u8string compressed;
|
||||
@@ -1089,6 +1137,10 @@ uint16_t PreparedNodeState::parseCondition(const std::string_view expression) {
|
||||
};
|
||||
|
||||
std::vector<std::variant<EnumTokenKind, std::string_view, int, uint16_t>> tokens;
|
||||
|
||||
if(expression.empty())
|
||||
tokens.push_back(int(1));
|
||||
|
||||
ssize_t pos = 0;
|
||||
auto skipWS = [&](){ while(pos<expression.size() && std::isspace((unsigned char) expression[pos])) ++pos; };
|
||||
|
||||
|
||||
@@ -516,6 +516,8 @@ struct PrecompiledTexturePipeline {
|
||||
std::vector<std::pair<std::string, std::string>> Assets;
|
||||
// Чистый код текстурных преобразований, локальные идентификаторы связаны с Assets
|
||||
std::u8string Pipeline;
|
||||
// Pipeline содержит исходный текст (tex ...), нужен для компиляции на сервере
|
||||
bool IsSource = false;
|
||||
};
|
||||
|
||||
struct TexturePipeline {
|
||||
@@ -530,15 +532,7 @@ struct TexturePipeline {
|
||||
};
|
||||
|
||||
// Компилятор текстурных потоков
|
||||
inline PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, const std::string_view defaultDomain = "core") {
|
||||
PrecompiledTexturePipeline result;
|
||||
|
||||
auto [domain, key] = parseDomainKey(cmd, defaultDomain);
|
||||
|
||||
result.Assets.emplace_back(domain, key);
|
||||
|
||||
return result;
|
||||
}
|
||||
PrecompiledTexturePipeline compileTexturePipeline(const std::string &cmd, std::string_view defaultDomain = "core");
|
||||
|
||||
struct NodestateEntry {
|
||||
std::string Name;
|
||||
|
||||
253
Src/Common/AssetsPreloader.hpp
Normal file
253
Src/Common/AssetsPreloader.hpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include "Common/Async.hpp"
|
||||
#include "TOSAsync.hpp"
|
||||
#include "boost/asio/executor.hpp"
|
||||
#include "boost/asio/experimental/channel.hpp"
|
||||
#include "boost/asio/this_coro.hpp"
|
||||
#include "sha2.hpp"
|
||||
|
||||
/*
|
||||
Класс отвечает за отслеживание изменений и подгрузки медиаресурсов в указанных директориях.
|
||||
Медиаресурсы, собранные из папки assets или зарегистрированные модами.
|
||||
Хранит все данные в оперативной памяти.
|
||||
*/
|
||||
|
||||
|
||||
enum class EnumAssets : int {
|
||||
Nodestate, Particle, Animation, Model, Texture, Sound, Font, MAX_ENUM
|
||||
};
|
||||
|
||||
using AssetsNodestate = uint32_t;
|
||||
using AssetsParticle = uint32_t;
|
||||
using AssetsAnimation = uint32_t;
|
||||
using AssetsModel = uint32_t;
|
||||
using AssetsTexture = uint32_t;
|
||||
using AssetsSound = uint32_t;
|
||||
using AssetsFont = uint32_t;
|
||||
|
||||
static constexpr const char* EnumAssetsToDirectory(EnumAssets value) {
|
||||
switch(value) {
|
||||
case EnumAssets::Nodestate: return "nodestate";
|
||||
case EnumAssets::Particle: return "particles";
|
||||
case EnumAssets::Animation: return "animations";
|
||||
case EnumAssets::Model: return "models";
|
||||
case EnumAssets::Texture: return "textures";
|
||||
case EnumAssets::Sound: return "sounds";
|
||||
case EnumAssets::Font: return "fonts";
|
||||
default:
|
||||
}
|
||||
|
||||
assert(!"Неизвестный тип медиаресурса");
|
||||
}
|
||||
|
||||
namespace LV {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct ResourceFile {
|
||||
using Hash_t = sha2::sha256_hash; // boost::uuids::detail::sha1::digest_type;
|
||||
|
||||
Hash_t Hash;
|
||||
std::vector<std::byte> Data;
|
||||
|
||||
void calcHash() {
|
||||
Hash = sha2::sha256((const uint8_t*) Data.data(), Data.size());
|
||||
}
|
||||
};
|
||||
|
||||
class AssetsPreloader : public TOS::IAsyncDestructible {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<AssetsPreloader>;
|
||||
|
||||
//
|
||||
struct ReloadResult {
|
||||
};
|
||||
|
||||
struct ReloadStatus {
|
||||
/// TODO: callback'и для обновления статусов
|
||||
/// TODO: многоуровневый статус std::vector<std::string>. Этапы/Шаги/Объекты
|
||||
};
|
||||
|
||||
public:
|
||||
static coro<Ptr> Create(asio::io_context& ioc);
|
||||
~AssetsPreloader() = default;
|
||||
|
||||
AssetsPreloader(const AssetsPreloader&) = delete;
|
||||
AssetsPreloader(AssetsPreloader&&) = delete;
|
||||
AssetsPreloader& operator=(const AssetsPreloader&) = delete;
|
||||
AssetsPreloader& operator=(AssetsPreloader&&) = delete;
|
||||
|
||||
// Пересматривает ресурсы и выдаёт изменения.
|
||||
// Одновременно можно работать только один такой вызов.
|
||||
// instances -> пути к директории с assets или архивы с assets внутри. От низшего приоритета к высшему.
|
||||
// status -> обратный отклик о процессе обновления ресурсов.
|
||||
// ReloadStatus <- новые и потерянные ресурсы.
|
||||
coro<ReloadResult> reloadResources(const std::vector<fs::path>& instances, ReloadStatus* status = nullptr) {
|
||||
bool expected = false;
|
||||
assert(Reloading_.compare_exchange_strong(expected, true) && "Двойной вызов reloadResources");
|
||||
|
||||
try {
|
||||
ReloadStatus secondStatus;
|
||||
co_return _reloadResources(instances, status ? *status : secondStatus);
|
||||
} catch(...) {
|
||||
assert(!"reloadResources: здесь не должно быть ошибок");
|
||||
}
|
||||
|
||||
Reloading_.exchange(false);
|
||||
}
|
||||
|
||||
private:
|
||||
struct ResourceFirstStageInfo {
|
||||
// Путь к архиву (если есть), и путь до ресурса
|
||||
fs::path ArchivePath, Path;
|
||||
// Время изменения файла
|
||||
fs::file_time_type Timestamp;
|
||||
};
|
||||
|
||||
struct ResourceSecondStageInfo : public ResourceFirstStageInfo {
|
||||
// Обезличенный ресурс
|
||||
std::shared_ptr<std::vector<uint8_t>> Resource;
|
||||
ResourceFile::Hash_t Hash;
|
||||
// Сырой заголовок
|
||||
std::vector<std::string> Dependencies;
|
||||
};
|
||||
|
||||
/*
|
||||
Ресурс имеет бинарную часть, из который вырезаны все зависимости.
|
||||
Вторая часть это заголовок, которые всегда динамично передаётся с сервера.
|
||||
В заголовке хранятся зависимости от ресурсов.
|
||||
*/
|
||||
struct MediaResource {
|
||||
std::string Domain, Key;
|
||||
|
||||
fs::file_time_type Timestamp;
|
||||
// Обезличенный ресурс
|
||||
std::shared_ptr<std::vector<uint8_t>> Resource;
|
||||
// Хэш ресурса
|
||||
ResourceFile::Hash_t Hash;
|
||||
|
||||
// Скомпилированный заголовок
|
||||
std::vector<uint8_t> Dependencies;
|
||||
};
|
||||
|
||||
AssetsPreloader(asio::io_context& ioc)
|
||||
: TOS::IAsyncDestructible(ioc)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Текущее состояние reloadResources
|
||||
std::atomic<bool> Reloading_ = false;
|
||||
|
||||
// Пересмотр ресурсов
|
||||
coro<ReloadResult> _reloadResources(const std::vector<fs::path>& instances, ReloadStatus& status) const {
|
||||
// 1) Поиск всех ресурсов и построение конечной карты ресурсов (timestamps, path, name, size)
|
||||
// Карта найденных ресурсов
|
||||
std::unordered_map<
|
||||
EnumAssets, // Тип ресурса
|
||||
std::unordered_map<
|
||||
std::string, // Domain
|
||||
std::unordered_map<
|
||||
std::string, // Key
|
||||
ResourceFirstStageInfo // ResourceInfo
|
||||
>
|
||||
>
|
||||
> resourcesFirstStage;
|
||||
|
||||
for (const fs::path& instance : instances) {
|
||||
try {
|
||||
if (fs::is_regular_file(instance)) {
|
||||
// Может архив
|
||||
/// TODO: пока не поддерживается
|
||||
} else if (fs::is_directory(instance)) {
|
||||
// Директория
|
||||
fs::path assets = instance / "assets";
|
||||
if (fs::exists(assets) && fs::is_directory(assets)) {
|
||||
// Директорию assets существует, перебираем домены в ней
|
||||
for (auto begin = fs::directory_iterator(assets), end = fs::directory_iterator(); begin != end; begin++) {
|
||||
if (!begin->is_directory())
|
||||
continue;
|
||||
|
||||
/// TODO: выглядит всё не очень асинхронно
|
||||
co_await asio::post(co_await asio::this_coro::executor);
|
||||
|
||||
fs::path domainPath = begin->path();
|
||||
std::string domain = domainPath.filename();
|
||||
|
||||
// Перебираем по типу ресурса
|
||||
for (EnumAssets assetType = EnumAssets(0); assetType < EnumAssets::MAX_ENUM; ((int&) assetType)++) {
|
||||
fs::path assetPath = domainPath / EnumAssetsToDirectory(assetType);
|
||||
|
||||
std::unordered_map<
|
||||
std::string, // Key
|
||||
ResourceFirstStageInfo // ResourceInfo
|
||||
>& firstStage = resourcesFirstStage[assetType][domain];
|
||||
|
||||
// Исследуем все ресурсы одного типа
|
||||
for (auto begin = fs::recursive_directory_iterator(assetPath), end = fs::recursive_directory_iterator(); begin != end; begin++) {
|
||||
if (begin->is_directory())
|
||||
continue;
|
||||
|
||||
fs::path file = begin->path();
|
||||
std::string key = fs::relative(file, domainPath).string();
|
||||
|
||||
// Работаем с ресурсом
|
||||
firstStage[key] = ResourceFirstStageInfo{
|
||||
.Path = file,
|
||||
.Timestamp = fs::last_write_time(file)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Неизвестный тип инстанса медиаресурсов");
|
||||
}
|
||||
} catch (const std::exception& exc) {
|
||||
/// TODO: Логгировать в статусе
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Обрабатываться будут только изменённые (новый timestamp) или новые ресурсы
|
||||
// .meta
|
||||
|
||||
// Текстуры, шрифты, звуки хранить как есть
|
||||
// У моделей, состояний нод, анимации, частиц обналичить зависимости
|
||||
// Мета влияет только на хедер
|
||||
|
||||
/// TODO: реализовать реформатирование новых и изменённых ресурсов во внутренний обезличенный формат
|
||||
|
||||
co_await asio::post(co_await asio::this_coro::executor);
|
||||
|
||||
asio::experimental::channel<void()> ch(IOC, 8);
|
||||
|
||||
co_return ReloadResult{};
|
||||
}
|
||||
|
||||
std::unordered_map<
|
||||
EnumAssets, // Тип ресурса
|
||||
std::unordered_map<
|
||||
std::string, // Domain
|
||||
std::unordered_map<
|
||||
std::string, // Key
|
||||
uint32_t // ResourceId
|
||||
>
|
||||
>
|
||||
> DKToId;
|
||||
|
||||
std::unordered_map<
|
||||
EnumAssets, // Тип ресурса
|
||||
std::unordered_map<
|
||||
uint32_t,
|
||||
MediaResource // ResourceInfo
|
||||
>
|
||||
> MediaResources;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user