Доработка пайплайн машины (требуется пересмотр технологии)

This commit is contained in:
2026-01-03 00:41:09 +06:00
parent f56b46f669
commit 776e9bfaca
31 changed files with 2684 additions and 601 deletions

View File

@@ -112,7 +112,6 @@ struct DefPortalInfo {
};
struct DefEntityInfo {
};
struct DefFuncEntityInfo {
@@ -138,7 +137,10 @@ struct PortalInfo {
};
struct EntityInfo {
DefEntityId DefId = 0;
WorldId_t WorldId = 0;
Pos::Object Pos = Pos::Object(0);
glm::quat Quat = glm::quat(1.f, 0.f, 0.f, 0.f);
};
struct FuncEntityInfo {
@@ -232,4 +234,4 @@ public:
virtual ~ISurfaceEventListener();
};
}
}

View File

@@ -606,10 +606,12 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
std::vector<DefWorldId> profile_World_Lost;
std::unordered_map<DefPortalId, void*> profile_Portal_AddOrChange;
std::vector<DefPortalId> profile_Portal_Lost;
std::unordered_map<DefEntityId, void*> profile_Entity_AddOrChange;
std::unordered_map<DefEntityId, DefEntityInfo> profile_Entity_AddOrChange;
std::vector<DefEntityId> profile_Entity_Lost;
std::unordered_map<DefItemId, void*> profile_Item_AddOrChange;
std::vector<DefItemId> profile_Item_Lost;
std::unordered_map<EntityId_t, EntityInfo> entity_AddOrChange;
std::vector<EntityId_t> entity_Lost;
{
for(TickData& data : ticks) {
@@ -726,6 +728,25 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
auto eraseIter = std::unique(profile_Item_Lost.begin(), profile_Item_Lost.end());
profile_Item_Lost.erase(eraseIter, profile_Item_Lost.end());
}
{
for(auto& [id, info] : data.Entity_AddOrChange) {
auto iter = std::lower_bound(entity_Lost.begin(), entity_Lost.end(), id);
if(iter != entity_Lost.end() && *iter == id)
entity_Lost.erase(iter);
entity_AddOrChange[id] = info;
}
for(EntityId_t id : data.Entity_Lost) {
entity_AddOrChange.erase(id);
}
entity_Lost.insert(entity_Lost.end(), data.Entity_Lost.begin(), data.Entity_Lost.end());
std::sort(entity_Lost.begin(), entity_Lost.end());
auto eraseIter = std::unique(entity_Lost.begin(), entity_Lost.end());
entity_Lost.erase(eraseIter, entity_Lost.end());
}
}
for(auto& [id, _] : profile_Voxel_AddOrChange)
@@ -829,6 +850,11 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
auto& c = chunks_Changed[wId];
for(auto& [pos, val] : list) {
auto& sizes = VisibleChunkCompressed[wId][pos];
VisibleChunkCompressedBytes -= sizes.Voxels;
sizes.Voxels = val.size();
VisibleChunkCompressedBytes += sizes.Voxels;
caocvr[pos] = unCompressVoxels(val);
c.push_back(pos);
}
@@ -839,6 +865,11 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
auto& c = chunks_Changed[wId];
for(auto& [pos, val] : list) {
auto& sizes = VisibleChunkCompressed[wId][pos];
VisibleChunkCompressedBytes -= sizes.Nodes;
sizes.Nodes = val.size();
VisibleChunkCompressedBytes += sizes.Nodes;
auto& chunkNodes = caocvr[pos];
unCompressNodes(val, chunkNodes.data());
debugCheckGeneratedChunkNodes(wId, pos, chunkNodes);
@@ -985,20 +1016,41 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
for(auto& [resId, def] : profile_Node_AddOrChange) {
Profiles.DefNode[resId] = def;
}
for(auto& [resId, def] : profile_Entity_AddOrChange) {
Profiles.DefEntity[resId] = def;
}
}
// Чанки
{
for(auto& [wId, lost] : regions_Lost_Result) {
auto iterWorld = Content.Worlds.find(wId);
if(iterWorld == Content.Worlds.end())
auto iterSizesWorld = VisibleChunkCompressed.find(wId);
if(iterWorld != Content.Worlds.end()) {
for(const Pos::GlobalRegion& rPos : lost) {
auto iterRegion = iterWorld->second.Regions.find(rPos);
if(iterRegion != iterWorld->second.Regions.end())
iterWorld->second.Regions.erase(iterRegion);
}
}
if(iterSizesWorld == VisibleChunkCompressed.end())
continue;
for(const Pos::GlobalRegion& rPos : lost) {
auto iterRegion = iterWorld->second.Regions.find(rPos);
if(iterRegion != iterWorld->second.Regions.end())
iterWorld->second.Regions.erase(iterRegion);
for(auto iter = iterSizesWorld->second.begin(); iter != iterSizesWorld->second.end(); ) {
if(Pos::GlobalRegion(iter->first >> 2) == rPos) {
VisibleChunkCompressedBytes -= iter->second.Voxels;
VisibleChunkCompressedBytes -= iter->second.Nodes;
iter = iterSizesWorld->second.erase(iter);
} else {
++iter;
}
}
}
if(iterSizesWorld->second.empty())
VisibleChunkCompressed.erase(iterSizesWorld);
}
for(auto& [wId, voxels] : chunks_AddOrChange_Voxel_Result) {
@@ -1019,6 +1071,43 @@ void ServerSession::update(GlobalTime gTime, float dTime) {
}
// Сущности
{
for(auto& [entityId, info] : entity_AddOrChange) {
auto iter = Content.Entityes.find(entityId);
if(iter != Content.Entityes.end() && iter->second.WorldId != info.WorldId) {
auto iterWorld = Content.Worlds.find(iter->second.WorldId);
if(iterWorld != Content.Worlds.end()) {
auto &list = iterWorld->second.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
}
Content.Entityes[entityId] = info;
auto &list = Content.Worlds[info.WorldId].Entitys;
if(std::find(list.begin(), list.end(), entityId) == list.end())
list.push_back(entityId);
}
for(EntityId_t entityId : entity_Lost) {
auto iter = Content.Entityes.find(entityId);
if(iter != Content.Entityes.end()) {
auto iterWorld = Content.Worlds.find(iter->second.WorldId);
if(iterWorld != Content.Worlds.end()) {
auto &list = iterWorld->second.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
Content.Entityes.erase(iter);
} else {
for(auto& [wId, worldInfo] : Content.Worlds) {
auto &list = worldInfo.Entitys;
list.erase(std::remove(list.begin(), list.end(), entityId), list.end());
}
}
}
}
if(RS)
RS->tickSync(result);
}
@@ -1402,11 +1491,18 @@ coro<> ServerSession::rP_Definition(Net::AsyncSocket &sock) {
co_return;
case ToClient::L2Definition::Entity:
{
DefEntityId id = co_await sock.read<DefEntityId>();
DefEntityInfo def;
AsyncContext.ThisTickEntry.Profile_Entity_AddOrChange.emplace_back(id, def);
co_return;
}
case ToClient::L2Definition::FreeEntity:
{
DefEntityId id = co_await sock.read<DefEntityId>();
AsyncContext.ThisTickEntry.Profile_Entity_Lost.push_back(id);
co_return;
}
default:
protocolError();
}
@@ -1433,11 +1529,35 @@ coro<> ServerSession::rP_Content(Net::AsyncSocket &sock) {
co_return;
case ToClient::L2Content::Entity:
{
EntityId_t id = co_await sock.read<EntityId_t>();
DefEntityId defId = co_await sock.read<DefEntityId>();
WorldId_t worldId = co_await sock.read<WorldId_t>();
Pos::Object pos;
pos.x = co_await sock.read<decltype(pos.x)>();
pos.y = co_await sock.read<decltype(pos.y)>();
pos.z = co_await sock.read<decltype(pos.z)>();
ToServer::PacketQuat q;
for(int iter = 0; iter < 5; iter++)
q.Data[iter] = co_await sock.read<uint8_t>();
EntityInfo info;
info.DefId = defId;
info.WorldId = worldId;
info.Pos = pos;
info.Quat = q.toQuat();
AsyncContext.ThisTickEntry.Entity_AddOrChange.emplace_back(id, info);
co_return;
}
case ToClient::L2Content::RemoveEntity:
{
EntityId_t id = co_await sock.read<EntityId_t>();
AsyncContext.ThisTickEntry.Entity_Lost.push_back(id);
co_return;
}
case ToClient::L2Content::ChunkVoxels:
{
WorldId_t wcId = co_await sock.read<WorldId_t>();

View File

@@ -42,6 +42,10 @@ public:
return Socket->isAlive() && IsConnected;
}
uint64_t getVisibleCompressedChunksBytes() const {
return VisibleChunkCompressedBytes;
}
// ISurfaceEventListener
virtual void onResize(uint32_t width, uint32_t height) override;
@@ -99,7 +103,7 @@ private:
std::vector<DefWorldId> Profile_World_Lost;
std::vector<std::pair<DefPortalId, void*>> Profile_Portal_AddOrChange;
std::vector<DefPortalId> Profile_Portal_Lost;
std::vector<std::pair<DefEntityId, void*>> Profile_Entity_AddOrChange;
std::vector<std::pair<DefEntityId, DefEntityInfo>> Profile_Entity_AddOrChange;
std::vector<DefEntityId> Profile_Entity_Lost;
std::vector<std::pair<DefItemId, void*>> Profile_Item_AddOrChange;
std::vector<DefItemId> Profile_Item_Lost;
@@ -110,6 +114,13 @@ private:
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::u8string>> Chunks_AddOrChange_Voxel;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, std::u8string>> Chunks_AddOrChange_Node;
std::unordered_map<WorldId_t, std::vector<Pos::GlobalRegion>> Regions_Lost;
std::vector<std::pair<EntityId_t, EntityInfo>> Entity_AddOrChange;
std::vector<EntityId_t> Entity_Lost;
};
struct ChunkCompressedSize {
uint32_t Voxels = 0;
uint32_t Nodes = 0;
};
struct AssetsBindsChange {
@@ -169,6 +180,9 @@ private:
GlobalTime LastSendPYR_POS;
std::unordered_map<WorldId_t, std::unordered_map<Pos::GlobalChunk, ChunkCompressedSize>> VisibleChunkCompressed;
uint64_t VisibleChunkCompressedBytes = 0;
// Приём данных с сокета
coro<> run(AsyncUseControl::Lock);
void protocolError();

View File

@@ -14,6 +14,34 @@ PipelinedTextureAtlas::AtlasTextureId PipelinedTextureAtlas::getByPipeline(const
_AddictedTextures[texId].push_back(pipeline);
}
{
std::vector<TexturePipelineProgram::AnimSpec> animMeta =
TexturePipelineProgram::extractAnimationSpecs(pipeline._Pipeline.data(), pipeline._Pipeline.size());
if (!animMeta.empty()) {
AnimatedPipelineState entry;
entry.Specs.reserve(animMeta.size());
for (const auto& spec : animMeta) {
detail::AnimSpec16 outSpec{};
outSpec.TexId = spec.HasTexId ? spec.TexId : TextureAtlas::kOverflowId;
outSpec.FrameW = spec.FrameW;
outSpec.FrameH = spec.FrameH;
outSpec.FrameCount = spec.FrameCount;
outSpec.FpsQ = spec.FpsQ;
outSpec.Flags = spec.Flags;
entry.Specs.push_back(outSpec);
}
entry.LastFrames.resize(entry.Specs.size(), std::numeric_limits<uint32_t>::max());
entry.Smooth = false;
for (const auto& spec : entry.Specs) {
if (spec.Flags & detail::AnimSmooth) {
entry.Smooth = true;
break;
}
}
_AnimatedPipelines.emplace(pipeline, std::move(entry));
}
}
return atlasTexId;
}
@@ -37,6 +65,7 @@ void PipelinedTextureAtlas::freeByPipeline(const HashedPipeline& pipeline) {
Super.removeTexture(iter->second);
_AtlasCpuTextures.erase(iter->second);
_PipeToTexId.erase(iter);
_AnimatedPipelines.erase(pipeline);
}
void PipelinedTextureAtlas::updateTexture(uint32_t texId, const StoredTexture& texture) {
@@ -90,7 +119,7 @@ StoredTexture PipelinedTextureAtlas::_generatePipelineTexture(const HashedPipeli
}
TexturePipelineProgram program;
program.fromWords(std::move(words));
program.fromBytes(std::move(words));
TexturePipelineProgram::OwnedTexture baked;
auto provider = [this](uint32_t texId) -> std::optional<Texture> {
@@ -109,7 +138,7 @@ StoredTexture PipelinedTextureAtlas::_generatePipelineTexture(const HashedPipeli
return tex;
};
if (!program.bake(provider, baked, nullptr)) {
if (!program.bake(provider, baked, _AnimTimeSeconds, nullptr)) {
if (auto tex = tryCopyFirstDependencyTexture(pipeline)) {
return *tex;
}
@@ -135,6 +164,7 @@ StoredTexture PipelinedTextureAtlas::_generatePipelineTexture(const HashedPipeli
void PipelinedTextureAtlas::flushNewPipelines() {
std::vector<uint32_t> changedTextures = std::move(_ChangedTextures);
_ChangedTextures.clear();
std::sort(changedTextures.begin(), changedTextures.end());
changedTextures.erase(std::unique(changedTextures.begin(), changedTextures.end()), changedTextures.end());
@@ -150,6 +180,7 @@ void PipelinedTextureAtlas::flushNewPipelines() {
}
changedPipelineTextures.append_range(std::move(_ChangedPipelines));
_ChangedPipelines.clear();
changedTextures.clear();
std::sort(changedPipelineTextures.begin(), changedPipelineTextures.end());
@@ -165,6 +196,18 @@ void PipelinedTextureAtlas::flushNewPipelines() {
auto& stored = _AtlasCpuTextures[atlasTexId];
stored = std::move(texture);
if (!stored._Pixels.empty()) {
// Смена порядка пикселей
for (uint32_t& pixel : stored._Pixels) {
union {
struct { uint8_t r, g, b, a; } color;
uint32_t data;
};
data = pixel;
std::swap(color.r, color.b);
pixel = data;
}
Super.setTextureData(atlasTexId,
stored._Widht,
stored._Height,
@@ -182,6 +225,72 @@ void PipelinedTextureAtlas::notifyGpuFinished() {
Super.notifyGpuFinished();
}
bool PipelinedTextureAtlas::updateAnimatedPipelines(double timeSeconds) {
_AnimTimeSeconds = timeSeconds;
if (_AnimatedPipelines.empty()) {
return false;
}
bool changed = false;
for (auto& [pipeline, entry] : _AnimatedPipelines) {
if (entry.Specs.empty()) {
continue;
}
if (entry.Smooth) {
_ChangedPipelines.push_back(pipeline);
changed = true;
continue;
}
if (entry.LastFrames.size() != entry.Specs.size())
entry.LastFrames.assign(entry.Specs.size(), std::numeric_limits<uint32_t>::max());
bool pipelineChanged = false;
for (size_t i = 0; i < entry.Specs.size(); ++i) {
const auto& spec = entry.Specs[i];
uint32_t fpsQ = spec.FpsQ ? spec.FpsQ : TexturePipelineProgram::DefaultAnimFpsQ;
double fps = double(fpsQ) / 256.0;
double frameTime = timeSeconds * fps;
if (frameTime < 0.0)
frameTime = 0.0;
uint32_t frameCount = spec.FrameCount;
// Авторасчёт количества кадров
if (frameCount == 0) {
auto iterTex = _ResToTexture.find(spec.TexId);
if (iterTex != _ResToTexture.end()) {
uint32_t fw = spec.FrameW ? spec.FrameW : iterTex->second._Widht;
uint32_t fh = spec.FrameH ? spec.FrameH : iterTex->second._Widht;
if (fw > 0 && fh > 0) {
if (spec.Flags & detail::AnimHorizontal)
frameCount = iterTex->second._Widht / fw;
else
frameCount = iterTex->second._Height / fh;
}
}
}
if (frameCount == 0)
frameCount = 1;
uint32_t frameIndex = frameCount ? (uint32_t(frameTime) % frameCount) : 0u;
if (entry.LastFrames[i] != frameIndex) {
entry.LastFrames[i] = frameIndex;
pipelineChanged = true;
}
}
if (pipelineChanged) {
_ChangedPipelines.push_back(pipeline);
changed = true;
}
}
return changed;
}
std::optional<StoredTexture> PipelinedTextureAtlas::tryCopyFirstDependencyTexture(const HashedPipeline& pipeline) const {
auto deps = pipeline.getDependencedTextures();
if (!deps.empty()) {

View File

@@ -6,6 +6,7 @@
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <optional>
#include <unordered_map>
#include <utility>
@@ -22,6 +23,7 @@ enum class Op16 : Word {
End = 0,
Base_Tex = 1,
Base_Fill = 2,
Base_Anim = 3,
Resize = 10,
Transform = 11,
Opacity = 12,
@@ -33,6 +35,7 @@ enum class Op16 : Word {
Multiply = 18,
Screen = 19,
Colorize = 20,
Anim = 21,
Overlay = 30,
Mask = 31,
LowPart = 32,
@@ -43,13 +46,24 @@ enum class SrcKind16 : Word { TexId = 0, Sub = 1 };
struct SrcRef16 {
SrcKind16 kind{SrcKind16::TexId};
Word a = 0;
Word b = 0;
uint32_t TexId = 0;
uint32_t Off = 0;
uint32_t Len = 0;
};
inline uint32_t makeU32(Word lo, Word hi) {
return uint32_t(lo) | (uint32_t(hi) << 16);
}
enum AnimFlags16 : Word {
AnimSmooth = 1 << 0,
AnimHorizontal = 1 << 1
};
struct AnimSpec16 {
uint32_t TexId = 0;
uint16_t FrameW = 0;
uint16_t FrameH = 0;
uint16_t FrameCount = 0;
uint16_t FpsQ = 0;
uint16_t Flags = 0;
};
inline void addUniqueDep(boost::container::small_vector<uint32_t, 8>& deps, uint32_t id) {
if (id == TextureAtlas::kOverflowId) {
@@ -60,16 +74,52 @@ inline void addUniqueDep(boost::container::small_vector<uint32_t, 8>& deps, uint
}
}
inline bool readSrc(const std::vector<Word>& words, size_t end, size_t& ip, SrcRef16& out) {
inline bool read16(const std::vector<Word>& words, size_t end, size_t& ip, uint16_t& out) {
if (ip + 1 >= end) {
return false;
}
out = uint16_t(words[ip]) | (uint16_t(words[ip + 1]) << 8);
ip += 2;
return true;
}
inline bool read24(const std::vector<Word>& words, size_t end, size_t& ip, uint32_t& out) {
if (ip + 2 >= end) {
return false;
}
out.kind = static_cast<SrcKind16>(words[ip++]);
out.a = words[ip++];
out.b = words[ip++];
out = uint32_t(words[ip]) |
(uint32_t(words[ip + 1]) << 8) |
(uint32_t(words[ip + 2]) << 16);
ip += 3;
return true;
}
inline bool read32(const std::vector<Word>& words, size_t end, size_t& ip, uint32_t& out) {
if (ip + 3 >= end) {
return false;
}
out = uint32_t(words[ip]) |
(uint32_t(words[ip + 1]) << 8) |
(uint32_t(words[ip + 2]) << 16) |
(uint32_t(words[ip + 3]) << 24);
ip += 4;
return true;
}
inline bool readSrc(const std::vector<Word>& words, size_t end, size_t& ip, SrcRef16& out) {
if (ip >= end) {
return false;
}
out.kind = static_cast<SrcKind16>(words[ip++]);
if (out.kind == SrcKind16::TexId) {
return read24(words, end, ip, out.TexId);
}
if (out.kind == SrcKind16::Sub) {
return read24(words, end, ip, out.Off) && read24(words, end, ip, out.Len);
}
return false;
}
inline void extractPipelineDependencies(const std::vector<Word>& words,
size_t start,
size_t end,
@@ -88,12 +138,12 @@ inline void extractPipelineDependencies(const std::vector<Word>& words,
auto need = [&](size_t n) { return ip + n <= end; };
auto handleSrc = [&](const SrcRef16& src) {
if (src.kind == SrcKind16::TexId) {
addUniqueDep(deps, makeU32(src.a, src.b));
addUniqueDep(deps, src.TexId);
return;
}
if (src.kind == SrcKind16::Sub) {
size_t subStart = static_cast<size_t>(src.a);
size_t subEnd = subStart + static_cast<size_t>(src.b);
size_t subStart = static_cast<size_t>(src.Off);
size_t subEnd = subStart + static_cast<size_t>(src.Len);
if (subStart < subEnd && subEnd <= words.size()) {
extractPipelineDependencies(words, subStart, subEnd, deps, visited);
}
@@ -108,37 +158,54 @@ inline void extractPipelineDependencies(const std::vector<Word>& words,
return;
case Op16::Base_Tex: {
if (!need(3)) return;
SrcRef16 src{};
if (!readSrc(words, end, ip, src)) return;
handleSrc(src);
} break;
case Op16::Base_Fill:
if (!need(4)) return;
ip += 4;
break;
case Op16::Base_Anim: {
SrcRef16 src{};
if (!readSrc(words, end, ip, src)) return;
handleSrc(src);
uint16_t tmp16 = 0;
uint8_t tmp8 = 0;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!need(1)) return;
tmp8 = words[ip++];
(void)tmp8;
} break;
case Op16::Base_Fill: {
uint16_t tmp16 = 0;
uint32_t tmp32 = 0;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!read32(words, end, ip, tmp32)) return;
} break;
case Op16::Overlay:
case Op16::Mask: {
if (!need(3)) return;
SrcRef16 src{};
if (!readSrc(words, end, ip, src)) return;
handleSrc(src);
} break;
case Op16::LowPart: {
if (!need(1 + 3)) return;
if (!need(1)) return;
ip += 1; // percent
SrcRef16 src{};
if (!readSrc(words, end, ip, src)) return;
handleSrc(src);
} break;
case Op16::Resize:
if (!need(2)) return;
ip += 2;
break;
case Op16::Resize: {
uint16_t tmp16 = 0;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
} break;
case Op16::Transform:
case Op16::Opacity:
@@ -151,8 +218,8 @@ inline void extractPipelineDependencies(const std::vector<Word>& words,
break;
case Op16::MakeAlpha:
if (!need(2)) return;
ip += 2;
if (!need(3)) return;
ip += 3;
break;
case Op16::Invert:
@@ -166,27 +233,42 @@ inline void extractPipelineDependencies(const std::vector<Word>& words,
break;
case Op16::Multiply:
case Op16::Screen:
if (!need(2)) return;
ip += 2;
break;
case Op16::Screen: {
uint32_t tmp32 = 0;
if (!read32(words, end, ip, tmp32)) return;
} break;
case Op16::Colorize:
if (!need(3)) return;
ip += 3;
break;
case Op16::Colorize: {
uint32_t tmp32 = 0;
if (!read32(words, end, ip, tmp32)) return;
if (!need(1)) return;
ip += 1;
} break;
case Op16::Anim: {
uint16_t tmp16 = 0;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!read16(words, end, ip, tmp16)) return;
if (!need(1)) return;
ip += 1;
} break;
case Op16::Combine: {
if (!need(3)) return;
ip += 2; // skip w,h
uint32_t n = words[ip++];
uint16_t w = 0, h = 0, n = 0;
if (!read16(words, end, ip, w)) return;
if (!read16(words, end, ip, h)) return;
if (!read16(words, end, ip, n)) return;
for (uint32_t i = 0; i < n; ++i) {
if (!need(2 + 3)) return;
ip += 2; // x, y
uint16_t tmp16 = 0;
if (!read16(words, end, ip, tmp16)) return; // x
if (!read16(words, end, ip, tmp16)) return; // y
SrcRef16 src{};
if (!readSrc(words, end, ip, src)) return;
handleSrc(src);
}
(void)w; (void)h;
} break;
default:
@@ -227,8 +309,9 @@ struct Pipeline {
_Pipeline = {
static_cast<detail::Word>(detail::Op16::Base_Tex),
static_cast<detail::Word>(detail::SrcKind16::TexId),
static_cast<detail::Word>(texId & 0xFFFFu),
static_cast<detail::Word>((texId >> 16) & 0xFFFFu),
static_cast<detail::Word>(texId & 0xFFu),
static_cast<detail::Word>((texId >> 8) & 0xFFu),
static_cast<detail::Word>((texId >> 16) & 0xFFu),
static_cast<detail::Word>(detail::Op16::End)
};
}
@@ -253,9 +336,7 @@ struct HashedPipeline {
constexpr std::size_t prime = 1099511628211ull;
for(detail::Word w : _Pipeline) {
hash ^= static_cast<uint8_t>(w & 0xFF);
hash *= prime;
hash ^= static_cast<uint8_t>((w >> 8) & 0xFF);
hash ^= static_cast<uint8_t>(w);
hash *= prime;
}
@@ -328,6 +409,13 @@ private:
std::vector<uint32_t> _ChangedTextures;
// Необходимые к созданию/обновлению пайплайны
std::vector<HashedPipeline> _ChangedPipelines;
struct AnimatedPipelineState {
std::vector<detail::AnimSpec16> Specs;
std::vector<uint32_t> LastFrames;
bool Smooth = false;
};
std::unordered_map<HashedPipeline, AnimatedPipelineState, HashedPipelineKeyHash, HashedPipelineKeyEqual> _AnimatedPipelines;
double _AnimTimeSeconds = 0.0;
public:
PipelinedTextureAtlas(TextureAtlas&& tk);
@@ -348,6 +436,26 @@ public:
return atlasLayers();
}
uint32_t maxLayers() const {
return Super.maxLayers();
}
uint32_t maxTextureId() const {
return Super.maxTextureId();
}
TextureAtlas::TextureId reservedOverflowId() const {
return Super.reservedOverflowId();
}
TextureAtlas::TextureId reservedLayerId(uint32_t layer) const {
return Super.reservedLayerId(layer);
}
void requestLayerCount(uint32_t layers) {
Super.requestLayerCount(layers);
}
// Должны всегда бронировать идентификатор, либо отдавать kOverflowId. При этом запись tex+pipeline остаётся
// Выдаёт стабильный идентификатор, привязанный к пайплайну
AtlasTextureId getByPipeline(const HashedPipeline& pipeline);
@@ -373,6 +481,8 @@ public:
void notifyGpuFinished();
bool updateAnimatedPipelines(double timeSeconds);
private:
std::optional<StoredTexture> tryCopyFirstDependencyTexture(const HashedPipeline& pipeline) const;

View File

@@ -29,11 +29,13 @@ TextureAtlas::TextureAtlas(VkDevice device,
EntriesCpu_.resize(Cfg_.MaxTextureId);
std::memset(EntriesCpu_.data(), 0, EntriesCpu_.size() * sizeof(Entry));
_initReservedEntries();
EntriesDirty_ = true;
Slots_.resize(Cfg_.MaxTextureId);
FreeIds_.reserve(Cfg_.MaxTextureId);
PendingInQueue_.assign(Cfg_.MaxTextureId, false);
NextId_ = _allocatableStart();
if(Cfg_.ExternalSampler != VK_NULL_HANDLE) {
Sampler_ = Cfg_.ExternalSampler;
@@ -70,13 +72,19 @@ TextureAtlas::TextureId TextureAtlas::registerTexture() {
_ensureAliveOrThrow();
TextureId id = kOverflowId;
if(NextId_ < _allocatableStart()) {
NextId_ = _allocatableStart();
}
while(!FreeIds_.empty() && isReservedId(FreeIds_.back())) {
FreeIds_.pop_back();
}
if(!FreeIds_.empty()) {
id = FreeIds_.back();
FreeIds_.pop_back();
} else if(NextId_ < Cfg_.MaxTextureId) {
} else if(NextId_ < _allocatableLimit()) {
id = NextId_++;
} else {
return kOverflowId;
return reservedOverflowId();
}
Slot& s = Slots_[id];
@@ -96,7 +104,7 @@ void TextureAtlas::setTextureData(TextureId id,
const void* pixelsRGBA8,
uint32_t rowPitchBytes) {
_ensureAliveOrThrow();
if(id == kOverflowId) return;
if(isInvalidId(id)) return;
_ensureRegisteredIdOrThrow(id);
if(w == 0 || h == 0) {
@@ -151,7 +159,7 @@ void TextureAtlas::setTextureData(TextureId id,
void TextureAtlas::clearTextureData(TextureId id) {
_ensureAliveOrThrow();
if(id == kOverflowId) return;
if(isInvalidId(id)) return;
_ensureRegisteredIdOrThrow(id);
Slot& s = Slots_[id];
@@ -172,7 +180,7 @@ void TextureAtlas::clearTextureData(TextureId id) {
void TextureAtlas::removeTexture(TextureId id) {
_ensureAliveOrThrow();
if(id == kOverflowId) return;
if(isInvalidId(id)) return;
_ensureRegisteredIdOrThrow(id);
Slot& s = Slots_[id];
@@ -217,7 +225,7 @@ TextureAtlas::DescriptorOut TextureAtlas::flushUploadsAndBarriers(VkCommandBuffe
while (!queue.empty()) {
TextureId id = queue.front();
queue.pop_front();
if(id == kOverflowId || id >= inQueue.size()) {
if(isInvalidId(id) || id >= inQueue.size()) {
continue;
}
if(!inQueue[id]) {
@@ -253,7 +261,7 @@ TextureAtlas::DescriptorOut TextureAtlas::flushUploadsAndBarriers(VkCommandBuffe
bool outOfSpace = false;
for(TextureId id : pendingNow) {
if(id == kOverflowId) continue;
if(isInvalidId(id)) continue;
if(id >= Slots_.size()) continue;
Slot& s = Slots_[id];
if(!s.InUse || !s.HasCpuData) continue;
@@ -310,7 +318,7 @@ TextureAtlas::DescriptorOut TextureAtlas::flushUploadsAndBarriers(VkCommandBuffe
};
for(TextureId id : pendingNow) {
if(id == kOverflowId) continue;
if(isInvalidId(id)) continue;
Slot& s = Slots_[id];
if(!s.InUse || !s.HasCpuData || !s.HasPlacement) continue;
if(!uploadTextureIntoAtlas(s, s.Place, Atlas_, false)) {

View File

@@ -10,7 +10,7 @@ TextureAtlas — как пользоваться (кратко)
2) Зарегистрируйте текстуру и получите стабильный ID:
TextureId id = atlas.registerTexture();
if(id == TextureAtlas::kOverflowId) { ... } // нет свободных ID
if(id == TextureAtlas::kOverflowId || id == atlas.reservedOverflowId()) { ... } // нет свободных ID
3) Задайте данные (RGBA8), можно много раз — ID не меняется:
atlas.setTextureData(id, w, h, pixels, rowPitchBytes);
@@ -32,7 +32,11 @@ TextureAtlas — как пользоваться (кратко)
atlas.removeTexture(id); // освободить ID (после этого использовать нельзя)
Примечания:
- Вызовы API с kOverflowId игнорируются (no-op).
- Вызовы API с kOverflowId и зарезервированными ID игнорируются (no-op).
- ID из начала диапазона зарезервированы под служебные нужды:
reservedOverflowId() == 0,
reservedLayerId(0) == 1 (первый слой),
reservedLayerId(1) == 2 (второй слой) и т.д.
- Ошибки ресурсов (нет места/стейджинга/oom) НЕ бросают исключения — дают события.
- Исключения только за неверный ввод/неверное использование (см. ТЗ).
- Класс не thread-safe: синхронизацию обеспечивает пользователь.
@@ -192,11 +196,60 @@ public:
/// Текущее число слоёв атласа.
uint32_t atlasLayers() const { return Atlas_.Layers; }
uint32_t maxLayers() const { return Cfg_.MaxLayers; }
uint32_t maxTextureId() const { return Cfg_.MaxTextureId; }
TextureId reservedOverflowId() const { return 0; }
TextureId reservedLayerId(uint32_t layer) const { return 1u + layer; }
bool isReservedId(TextureId id) const {
if(id >= Cfg_.MaxTextureId) return false;
return id < _reservedCount();
}
bool isReservedLayerId(TextureId id) const {
if(id >= Cfg_.MaxTextureId) return false;
return id != reservedOverflowId() && id < _reservedCount();
}
bool isInvalidId(TextureId id) const {
return id == kOverflowId || isReservedId(id);
}
void requestLayerCount(uint32_t layers) {
_ensureAliveOrThrow();
_scheduleLayerGrow(layers);
}
/// Общий staging-буфер (может быть задан извне).
std::shared_ptr<SharedStagingBuffer> getStagingBuffer() const { return Staging_; }
private:
void _moveFrom(TextureAtlas&& other) noexcept;
uint32_t _reservedCount() const { return Cfg_.MaxLayers + 1; }
uint32_t _reservedStart() const { return 0; }
uint32_t _allocatableStart() const { return _reservedCount(); }
uint32_t _allocatableLimit() const { return Cfg_.MaxTextureId; }
void _initReservedEntries() {
if(Cfg_.MaxTextureId <= _reservedCount()) {
return;
}
_setEntryInvalid(reservedOverflowId(), /*diagPending*/false, /*diagTooLarge*/false);
for(uint32_t layer = 0; layer < Cfg_.MaxLayers; ++layer) {
TextureId id = reservedLayerId(layer);
Entry& e = EntriesCpu_[id];
e.UVMinMax[0] = 0.0f;
e.UVMinMax[1] = 0.0f;
e.UVMinMax[2] = 1.0f;
e.UVMinMax[3] = 1.0f;
e.Layer = layer;
e.Flags = ENTRY_VALID;
}
EntriesDirty_ = true;
}
// ============================= Ошибки/валидация =============================
struct InputError : std::runtime_error {
@@ -225,6 +278,9 @@ private:
if(Cfg_.MaxTextureId == 0) {
throw _inputError("Config.MaxTextureId must be > 0");
}
if(Cfg_.MaxTextureId <= (Cfg_.MaxLayers + 1)) {
throw _inputError("Config.MaxTextureId must be > MaxLayers + 1 (reserved ids)");
}
if(Cfg_.MaxTextureSize != 2048) {
/// TODO:
@@ -246,7 +302,7 @@ private:
}
void _ensureRegisteredIdOrThrow(TextureId id) const {
if(id >= Cfg_.MaxTextureId) {
if(id >= Cfg_.MaxTextureId || isReservedId(id)) {
throw _inputError("TextureId out of range");
}
if(!Slots_[id].InUse || Slots_[id].StateValue == State::REMOVED) {

File diff suppressed because it is too large Load Diff

View File

@@ -311,7 +311,7 @@ void Vulkan::run()
}
if(Game.RSession) {
Game.RSession->beforeDraw();
Game.RSession->beforeDraw(double(gTime));
}
{
@@ -624,12 +624,6 @@ void Vulkan::run()
err = vkQueuePresentKHR(*lockQueue, &present);
}
{
auto lockQueue = Graphics.DeviceQueueGraphic.lock();
vkDeviceWaitIdle(Graphics.Device);
lockQueue.unlock();
}
if (err == VK_ERROR_OUT_OF_DATE_KHR)
{
freeSwapchains();
@@ -651,12 +645,6 @@ void Vulkan::run()
Screen.State = DrawState::End;
}
{
auto lockQueue = Graphics.DeviceQueueGraphic.lock();
vkDeviceWaitIdle(Graphics.Device);
lockQueue.unlock();
}
for(int iter = 0; iter < 4; iter++) {
vkDestroySemaphore(Graphics.Device, SemaphoreImageAcquired[iter], nullptr);
vkDestroySemaphore(Graphics.Device, SemaphoreDrawComplete[iter], nullptr);
@@ -688,8 +676,6 @@ uint32_t Vulkan::memoryTypeFromProperties(uint32_t bitsOfAcceptableTypes, VkFlag
void Vulkan::freeSwapchains()
{
//vkDeviceWaitIdle(Screen.Device);
if(Graphics.Instance && Graphics.Device)
{
std::vector<VkImageView> oldViews;
@@ -2302,6 +2288,9 @@ void Vulkan::gui_ConnectedToServer() {
(int) Game.RSession->PlayerPos.x >> 6, (int) Game.RSession->PlayerPos.y >> 6, (int) Game.RSession->PlayerPos.z >> 6
);
double chunksKb = double(Game.Session->getVisibleCompressedChunksBytes()) / 1024.0;
ImGui::Text("chunks compressed: %.1f KB", chunksKb);
if(ImGui::Button("Delimeter"))
LOG.debug();

View File

@@ -323,7 +323,7 @@ void ChunkMeshGenerator::run(uint8_t id) {
};
std::unordered_map<uint32_t, ModelCacheEntry> modelCache;
std::unordered_map<AssetsTexture, uint16_t> baseTextureCache;
std::unordered_map<AssetsTexture, uint32_t> baseTextureCache;
std::vector<NodeStateInfo> metaStatesInfo;
{
@@ -451,7 +451,7 @@ void ChunkMeshGenerator::run(uint8_t id) {
if(iterTex != baseTextureCache.end()) {
v.Tex = iterTex->second;
} else {
uint16_t resolvedTex = NSP->getTextureId(node->TexId);
uint32_t resolvedTex = NSP->getTextureId(node->TexId);
v.Tex = resolvedTex;
baseTextureCache.emplace(node->TexId, resolvedTex);
}
@@ -1670,7 +1670,7 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
}
if(TP) {
std::vector<std::tuple<AssetsTexture, Resource>> textureResources;
std::vector<TextureProvider::TextureUpdate> textureResources;
std::vector<AssetsTexture> textureLost;
if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Texture); iter != data.Assets_ChangeOrAdd.end()) {
@@ -1680,7 +1680,12 @@ void VulkanRenderSession::tickSync(const TickSyncData& data) {
if(entryIter == list.end())
continue;
textureResources.emplace_back(id, entryIter->second.Res);
textureResources.push_back({
.Id = id,
.Res = entryIter->second.Res,
.Domain = entryIter->second.Domain,
.Key = entryIter->second.Key
});
}
}
@@ -1739,9 +1744,9 @@ void VulkanRenderSession::setCameraPos(WorldId_t worldId, Pos::Object pos, glm::
PlayerPos /= float(Pos::Object_t::BS);
}
void VulkanRenderSession::beforeDraw() {
void VulkanRenderSession::beforeDraw(double timeSeconds) {
if(TP)
TP->update();
TP->update(timeSeconds);
LightDummy.atlasUpdateDynamicData();
CP.flushUploadsAndBarriers(VkInst->Graphics.CommandBufferRender);
}
@@ -1901,6 +1906,7 @@ void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuff
vkCmdDraw(drawCmd, 6*3*2, 1, 0, 0);
{
PCO.Model = glm::mat4(1.0f);
Pos::GlobalChunk x64offset = X64Offset >> Pos::Object_t::BS_Bit >> 4;
Pos::GlobalRegion x64offset_region = x64offset >> 2;
@@ -1940,9 +1946,160 @@ void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuff
PCO.Model = orig;
}
vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, NodeStaticTransparentPipeline);
vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO);
vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
MainAtlas_LightMap_PipelineLayout, 0, 2,
(const VkDescriptorSet[]) {TP ? TP->getDescriptorSet() : VK_NULL_HANDLE, VoxelLightMapDescriptor}, 0, nullptr);
ensureAtlasLayerPreview();
if(AtlasLayersPreview) {
glm::mat4 previewModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 64.0f, 0.0f)-glm::vec3(X64Offset >> Pos::Object_t::BS_Bit));
vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &previewModel);
VkBuffer previewBuffer = *AtlasLayersPreview;
VkDeviceSize previewOffset = 0;
vkCmdBindVertexBuffers(drawCmd, 0, 1, &previewBuffer, &previewOffset);
vkCmdDraw(drawCmd, AtlasLayersPreviewCount * 6, 1, 0, 0);
}
if(false) {
ensureEntityTexture();
if(!ServerSession->Content.Entityes.empty()) {
VkBuffer entityBuffer = TestQuad;
VkDeviceSize entityOffset = 0;
vkCmdBindVertexBuffers(drawCmd, 0, 1, &entityBuffer, &entityOffset);
glm::mat4 orig = PCO.Model;
for(const auto& pair : ServerSession->Content.Entityes) {
const auto& info = pair.second;
if(info.WorldId != WorldId)
continue;
glm::vec3 entityPos = Pos::Object_t::asFloatVec(info.Pos - X64Offset);
entityPos.y -= 1.6f; // Camera position arrives as eye height.
glm::mat4 model = glm::translate(glm::mat4(1.0f), entityPos);
model = model * glm::mat4(info.Quat);
model = glm::scale(model, glm::vec3(0.6f, 1.8f, 0.6f));
PCO.Model = model;
vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &PCO.Model);
vkCmdDraw(drawCmd, 6*3*2, 1, 0, 0);
}
PCO.Model = orig;
}
}
CP.pushFrame();
}
void VulkanRenderSession::updateTestQuadTexture(uint32_t texId) {
if(EntityTextureReady && EntityTextureId == texId)
return;
auto *array = reinterpret_cast<NodeVertexStatic*>(TestQuad.mapMemory());
const size_t vertexCount = TestQuad.getSize() / sizeof(NodeVertexStatic);
for(size_t iter = 0; iter < vertexCount; ++iter)
array[iter].Tex = texId;
TestQuad.unMapMemory();
EntityTextureId = texId;
EntityTextureReady = true;
}
void VulkanRenderSession::ensureEntityTexture() {
if(EntityTextureReady || !TP || !NSP)
return;
auto iter = ServerSession->Assets.find(EnumAssets::Texture);
if(iter == ServerSession->Assets.end() || iter->second.empty())
return;
const AssetEntry* picked = nullptr;
for(const auto& [id, entry] : iter->second) {
if(entry.Key == "default.png") {
picked = &entry;
break;
}
}
if(!picked) {
for(const auto& [id, entry] : iter->second) {
if(entry.Key == "grass.png") {
picked = &entry;
break;
}
}
}
if(!picked)
picked = &iter->second.begin()->second;
updateTestQuadTexture(NSP->getTextureId(picked->Id));
}
void VulkanRenderSession::ensureAtlasLayerPreview() {
if(!TP)
return;
const uint32_t maxLayers = TP->getAtlasMaxLayers();
if(maxLayers == 0)
return;
if(AtlasLayersPreview && AtlasLayersPreviewCount == maxLayers)
return;
TP->requestAtlasLayerCount(maxLayers);
const uint32_t vertsPerQuad = 6;
const uint32_t totalVerts = maxLayers * vertsPerQuad;
std::vector<NodeVertexStatic> verts(totalVerts);
const uint32_t columns = 4;
const uint32_t base = 224;
const uint32_t step = 320;
const uint32_t size = 256;
const uint32_t z = base;
auto makeVert = [&](uint32_t fx, uint32_t fy, uint32_t tex, uint32_t tu, uint32_t tv) -> NodeVertexStatic {
NodeVertexStatic v{};
v.FX = fx;
v.FY = fy;
v.FZ = z;
v.LS = 0;
v.Tex = tex;
v.TU = tu;
v.TV = tv;
return v;
};
for(uint32_t layer = 0; layer < maxLayers; ++layer) {
const uint32_t col = layer % columns;
const uint32_t row = layer / columns;
const uint32_t x0 = base + col * step;
const uint32_t y0 = base + row * step;
const uint32_t x1 = x0 + size;
const uint32_t y1 = y0 + size;
const uint32_t tex = TP->getAtlasLayerId(layer);
const size_t start = static_cast<size_t>(layer) * vertsPerQuad;
verts[start + 0] = makeVert(x0, y0, tex, 0, 0);
verts[start + 1] = makeVert(x0, y1, tex, 0, 65535);
verts[start + 2] = makeVert(x1, y1, tex, 65535, 65535);
verts[start + 3] = makeVert(x0, y0, tex, 0, 0);
verts[start + 4] = makeVert(x1, y1, tex, 65535, 65535);
verts[start + 5] = makeVert(x1, y0, tex, 65535, 0);
}
AtlasLayersPreview.emplace(VkInst, verts.size() * sizeof(NodeVertexStatic));
std::memcpy(AtlasLayersPreview->mapMemory(), verts.data(), verts.size() * sizeof(NodeVertexStatic));
AtlasLayersPreview->unMapMemory();
AtlasLayersPreviewCount = maxLayers;
}
void VulkanRenderSession::pushStage(EnumRenderStage stage) {
}

View File

@@ -418,6 +418,13 @@ private:
*/
class TextureProvider {
public:
struct TextureUpdate {
AssetsTexture Id = 0;
Resource Res;
std::string Domain;
std::string Key;
};
TextureProvider(Vulkan* inst, VkDescriptorPool descPool)
: Inst(inst), DescPool(descPool)
{
@@ -508,32 +515,58 @@ public:
return Descriptor;
}
uint16_t getTextureId(const TexturePipeline& pipe) {
uint32_t getTextureId(const TexturePipeline& pipe) {
std::lock_guard lock(Mutex);
auto iter = PipelineToAtlas.find(pipe);
if(iter != PipelineToAtlas.end())
return iter->second;
bool animated = isAnimatedPipeline(pipe);
if(!animated) {
auto iter = PipelineToAtlas.find(pipe);
if(iter != PipelineToAtlas.end())
return iter->second;
}
::HashedPipeline hashed = makeHashedPipeline(pipe);
uint32_t atlasId = Atlas->getByPipeline(hashed);
uint16_t result = 0;
if(atlasId <= std::numeric_limits<uint16_t>::max())
result = static_cast<uint16_t>(atlasId);
else
uint32_t result = atlasId;
if(Atlas && result >= Atlas->maxTextureId()) {
LOG.warn() << "Atlas texture id overflow: " << atlasId;
result = Atlas->reservedOverflowId();
}
PipelineToAtlas.emplace(pipe, result);
if(!animated)
PipelineToAtlas.emplace(pipe, result);
NeedsUpload = true;
return result;
}
uint32_t getAtlasMaxLayers() const {
std::lock_guard lock(Mutex);
return Atlas ? Atlas->maxLayers() : 0u;
}
uint32_t getAtlasLayerId(uint32_t layer) const {
std::lock_guard lock(Mutex);
if(!Atlas)
return TextureAtlas::kOverflowId;
if(layer >= Atlas->maxLayers())
return Atlas->reservedOverflowId();
return Atlas->reservedLayerId(layer);
}
void requestAtlasLayerCount(uint32_t layers) {
std::lock_guard lock(Mutex);
if(Atlas)
Atlas->requestLayerCount(layers);
}
// Применяет изменения, возвращая все затронутые модели
std::vector<AssetsTexture> onTexturesChanges(std::vector<std::tuple<AssetsTexture, Resource>> newOrChanged, std::vector<AssetsTexture> lost) {
std::vector<AssetsTexture> onTexturesChanges(std::vector<TextureUpdate> newOrChanged, std::vector<AssetsTexture> lost) {
std::lock_guard lock(Mutex);
std::vector<AssetsTexture> result;
for(const auto& [key, res] : newOrChanged) {
for(const auto& update : newOrChanged) {
const AssetsTexture key = update.Id;
const Resource& res = update.Res;
result.push_back(key);
iResource sres((const uint8_t*) res.data(), res.size());
@@ -563,12 +596,19 @@ public:
std::move(pixels)
));
if(auto anim = getDefaultAnimation(update.Key, width, height)) {
AnimatedSources[key] = *anim;
} else {
AnimatedSources.erase(key);
}
NeedsUpload = true;
}
for(AssetsTexture key : lost) {
result.push_back(key);
Atlas->freeTexture(key);
AnimatedSources.erase(key);
NeedsUpload = true;
}
@@ -579,9 +619,15 @@ public:
return result;
}
void update() {
void update(double timeSeconds) {
std::lock_guard lock(Mutex);
if(!NeedsUpload || !Atlas)
if(!Atlas)
return;
if(Atlas->updateAnimatedPipelines(timeSeconds))
NeedsUpload = true;
if(!NeedsUpload)
return;
Atlas->flushNewPipelines();
@@ -638,24 +684,94 @@ public:
}
private:
struct AnimatedSource {
uint16_t FrameW = 0;
uint16_t FrameH = 0;
uint16_t FrameCount = 0;
uint16_t FpsQ = 0;
uint16_t Flags = 0;
};
static std::optional<AnimatedSource> getDefaultAnimation(std::string_view key, uint32_t width, uint32_t height) {
if(auto slash = key.find_last_of('/'); slash != std::string_view::npos)
key = key.substr(slash + 1);
if(key == "fire_0.png") {
AnimatedSource anim;
anim.FrameW = static_cast<uint16_t>(width);
anim.FrameH = static_cast<uint16_t>(width);
anim.FrameCount = static_cast<uint16_t>(width ? height / width : 0);
anim.FpsQ = static_cast<uint16_t>(12 * 256);
anim.Flags = 0;
return anim;
}
if(key == "lava_still.png") {
AnimatedSource anim;
anim.FrameW = static_cast<uint16_t>(width);
anim.FrameH = static_cast<uint16_t>(width);
anim.FrameCount = static_cast<uint16_t>(width ? height / width : 0);
anim.FpsQ = static_cast<uint16_t>(8 * 256);
anim.Flags = 0;
return anim;
}
if(key == "water_still.png") {
AnimatedSource anim;
anim.FrameW = static_cast<uint16_t>(width);
anim.FrameH = static_cast<uint16_t>(width);
anim.FrameCount = static_cast<uint16_t>(width ? height / width : 0);
anim.FpsQ = static_cast<uint16_t>(8 * 256);
anim.Flags = TexturePipelineProgram::AnimSmooth;
return anim;
}
return std::nullopt;
}
bool isAnimatedPipeline(const TexturePipeline& pipe) const {
if(!pipe.Pipeline.empty())
return false;
if(pipe.BinTextures.size() != 1)
return false;
return AnimatedSources.contains(pipe.BinTextures.front());
}
::HashedPipeline makeHashedPipeline(const TexturePipeline& pipe) const {
::Pipeline pipeline;
if(!pipe.Pipeline.empty() && (pipe.Pipeline.size() % 2u) == 0u) {
std::vector<TexturePipelineProgram::Word> words;
words.reserve(pipe.Pipeline.size() / 2u);
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(pipe.Pipeline.data());
for(size_t i = 0; i < pipe.Pipeline.size(); i += 2) {
uint16_t lo = bytes[i];
uint16_t hi = bytes[i + 1];
words.push_back(static_cast<TexturePipelineProgram::Word>(lo | (hi << 8)));
}
pipeline._Pipeline.assign(words.begin(), words.end());
if(!pipe.Pipeline.empty()) {
const auto* bytes = reinterpret_cast<const ::detail::Word*>(pipe.Pipeline.data());
pipeline._Pipeline.assign(bytes, bytes + pipe.Pipeline.size());
}
if(pipeline._Pipeline.empty()) {
if(!pipe.BinTextures.empty())
pipeline = ::Pipeline(pipe.BinTextures.front());
if(!pipe.BinTextures.empty()) {
AssetsTexture texId = pipe.BinTextures.front();
auto animIter = AnimatedSources.find(texId);
if(animIter != AnimatedSources.end()) {
const auto& anim = animIter->second;
pipeline._Pipeline.clear();
pipeline._Pipeline.reserve(1 + 1 + 3 + 2 + 2 + 2 + 2 + 1 + 1);
auto emit16 = [&](uint16_t v) {
pipeline._Pipeline.push_back(static_cast<::detail::Word>(v & 0xFFu));
pipeline._Pipeline.push_back(static_cast<::detail::Word>((v >> 8) & 0xFFu));
};
pipeline._Pipeline.push_back(static_cast<::detail::Word>(::detail::Op16::Base_Anim));
pipeline._Pipeline.push_back(static_cast<::detail::Word>(::detail::SrcKind16::TexId));
pipeline._Pipeline.push_back(static_cast<::detail::Word>(texId & 0xFFu));
pipeline._Pipeline.push_back(static_cast<::detail::Word>((texId >> 8) & 0xFFu));
pipeline._Pipeline.push_back(static_cast<::detail::Word>((texId >> 16) & 0xFFu));
emit16(anim.FrameW);
emit16(anim.FrameH);
emit16(anim.FrameCount);
emit16(anim.FpsQ);
pipeline._Pipeline.push_back(static_cast<::detail::Word>(anim.Flags & 0xFFu));
pipeline._Pipeline.push_back(static_cast<::detail::Word>(::detail::Op16::End));
} else {
pipeline = ::Pipeline(texId);
}
}
}
return ::HashedPipeline(pipeline);
@@ -690,7 +806,8 @@ private:
std::shared_ptr<SharedStagingBuffer> AtlasStaging;
std::unique_ptr<PipelinedTextureAtlas> Atlas;
std::unordered_map<TexturePipeline, uint16_t> PipelineToAtlas;
std::unordered_map<TexturePipeline, uint32_t> PipelineToAtlas;
std::unordered_map<AssetsTexture, AnimatedSource> AnimatedSources;
bool NeedsUpload = false;
Logger LOG = "Client>TextureProvider";
@@ -811,7 +928,7 @@ public:
}
std::vector<std::vector<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>>> result;
std::unordered_map<TexturePipeline, uint16_t> pipelineResolveCache;
std::unordered_map<TexturePipeline, uint32_t> pipelineResolveCache;
auto appendModel = [&](AssetsModel modelId, const std::vector<Transformation>& transforms, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>& out) {
ModelProvider::Model model = MP.getModel(modelId);
@@ -886,7 +1003,7 @@ public:
return result;
}
uint16_t getTextureId(AssetsTexture texId) {
uint32_t getTextureId(AssetsTexture texId) {
if(texId == 0)
return 0;
@@ -1134,6 +1251,10 @@ class VulkanRenderSession : public IRenderSession {
AtlasImage LightDummy;
Buffer TestQuad;
std::optional<Buffer> TestVoxel;
std::optional<Buffer> AtlasLayersPreview;
uint32_t AtlasLayersPreviewCount = 0;
uint32_t EntityTextureId = 0;
bool EntityTextureReady = false;
VkDescriptorPool DescriptorPool = VK_NULL_HANDLE;
@@ -1187,7 +1308,7 @@ public:
return glm::translate(glm::mat4(quat), camOffset);
}
void beforeDraw();
void beforeDraw(double timeSeconds);
void onGpuFinished();
void drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd);
void pushStage(EnumRenderStage stage);
@@ -1195,6 +1316,9 @@ public:
static std::vector<VoxelVertexPoint> generateMeshForVoxelChunks(const std::vector<VoxelCube>& cubes);
private:
void updateTestQuadTexture(uint32_t texId);
void ensureEntityTexture();
void ensureAtlasLayerPreview();
void updateDescriptor_VoxelsLight();
void updateDescriptor_ChunksLight();
};