Доработка пайплайн машины (требуется пересмотр технологии)
This commit is contained in:
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user