1#include "Foundation/Logging/Logger.h"
6#include "ExtensionRegistry.h"
7#include "Resources/DataFetcherManager.h"
8#include "Resources/TextureManager.h"
10#include "Image360System.h"
14 using namespace Cogs::Core::Image360;
20 system = ExtensionRegistry::getExtensionSystem<Image360System>(context);
29 LOG_DEBUG(logger,
"[instance=%u] Failed to lookup instance.", instanceId);
37void Cogs::Core::Image360::Fetcher::issueChannelFetch(
Context* context,
42 const uint8_t channelIx,
43 const uint8_t revision,
44 const size_t cacheLevel,
45 const size_t cacheLevelIndex)
47 assert(channelIx < config.
channels.size());
50 std::vector<char> prefix(config.
treeDepth + 1, 0);
51 size_t t = cacheLevelIndex;
52 for (
size_t i = cacheLevel; i; --i) {
53 prefix[i] =
'0' + (t % 4);
56 prefix[0] =
'0' + char(t);
58 std::string path = valueChannel.prefix;
59 path.append(prefix.data());
61 bool loadAsData =
false;
62 switch (valueChannel.dataType) {
76 path.append(
".u16.zst");
80 assert(
false &&
"Invalid enum");
84 LOG_TRACE(logger,
"Map=%zu slot=%d rev=%u channel=%u: Issue fetching %s", mapIx, slotIx, revision, channelIx, path.c_str());
86 assert(!(loadItem.mapIx == mapIx && loadItem.slotIx == slotIx && loadItem.channelIx == channelIx && loadItem.revision == revision));
89 if (itemsLoading.empty()) {
95 loadItem.mapIx = mapIx;
96 loadItem.slotIx = slotIx;
97 loadItem.channelIx = channelIx;
98 loadItem.revision = revision;
101 auto handler = [context, instanceId = config.
instanceId, w = config.
baseSize, channelIx, mapIx, slotIx, revision, dataType = valueChannel.dataType](std::unique_ptr<Cogs::FileContents> contents1)
103 LOG_TRACE(logger,
"Map=%zu slot=%d rev=%u channel=%u Got data", mapIx, slotIx, revision, channelIx);
105 bool decompress =
false;
106 size_t elementSize = 0;
107 Cogs::TextureFormat format = Cogs::TextureFormat::Unknown;
110 format = Cogs::DataFormat::R8G8B8_UNORM_SRGB;
111 elementSize = 3 *
sizeof(uint8_t);
114 format = Cogs::DataFormat::R8G8B8A8_UNORM_SRGB;
115 elementSize = 4 *
sizeof(uint8_t);
118 format = Cogs::TextureFormat::R16_UINT;
119 elementSize =
sizeof(uint16_t);
122 format = Cogs::TextureFormat::R16_UINT;
123 elementSize =
sizeof(uint16_t);
127 assert(
false &&
"Invalid enum");
132 size_t expectedSize = elementSize * w * w;
133 std::unique_ptr<Cogs::Memory::MemoryBuffer> contents2 = std::make_unique<Cogs::Memory::MemoryBuffer>();
136 contents2->resize(expectedSize);
137 size_t outSize = ZSTD_decompress(contents2->data(), contents2->size(), contents1->ptr, contents1->size);
138 if (outSize != expectedSize) {
139 LOG_ERROR(logger,
"Error decompressing zstd data: %zxu", outSize);
145 contents2->swap(data_);
151 if (contents2->size() == expectedSize) {
153 loadInfo->target = Cogs::ResourceDimensions::Texture2D;
155 loadInfo->height = w;
156 loadInfo->format = format;
158 loadInfo->mipMaps =
false;
159 loadInfo->
resourceData.assign((
const char*)contents2->data(), (
const char*)contents2->data() + contents2->size());
160 texture = context->textureManager->loadTexture(loadInfo);
162 else if(!contents2->empty()) {
163 LOG_ERROR_ONCE(logger,
"Unexpected size of data (got=%zu, expected=%zu)", contents2->size(), expectedSize);
167 auto task = [context, instanceId, channelIx, mapIx, slotIx, revision, texture, contents3 = contents2.release()]
169 std::unique_ptr<Memory::MemoryBuffer> contents4(contents3);
174 if (!lookupAndCheckForStaleness(context, system, comp, data, instanceId)) {
175 LOG_WARNING(logger,
"Map=%zu slot=%d rev=%u channel=%u: Ignoring stale tile data file", mapIx, slotIx, revision, channelIx);
180 if ((loadItem.mapIx == mapIx) &&
181 (loadItem.slotIx == slotIx) &&
182 (loadItem.channelIx == channelIx) &&
183 (loadItem.revision == revision))
186 loadItem.texture = texture;
187 loadItem.buffer = std::move(contents4);
190 loadItem.failed =
true;
192 context->
engine->setDirty();
197 LOG_WARNING(logger,
"Map=%zu slot=%d rev=%u channel=%u: Failed to find matching loadItem", mapIx, slotIx, revision, channelIx);
200 context->
engine->runTaskInMainThread(std::move(task));
202 loadItem.fetchId = DataFetcherManager::fetchAsync(context, path, handler, 0, 0,
true, Cogs::FileContentsHints::None);
211bool Cogs::Core::Image360::Fetcher::canLoadAnyItems(
const Config& config)
const
213 return itemsLoading.size() < config.maxConcurrent;
217void Cogs::Core::Image360::Fetcher::processLoadItems(
Context* context,
221 const uint32_t currentFrame)
223 itemsLoadingNext.clear();
224 for (LoadItem& loadItem : itemsLoading) {
226 SlotIx slotIx = cache.map[loadItem.mapIx];
227 if (0 <= slotIx && loadItem.slotIx == slotIx && cache.items[slotIx].revision == loadItem.revision) {
231 bool isValue = loadItem.channelIx == config.
valueChannel;
233 const uint32_t age = currentFrame - cacheItem.lastTouched;
235 if (!isValue && !isDepth) {
236 LOG_ERROR(logger,
"Map=%zu Slot=%d channel=%u rev=%u channel is neither value nor depth", loadItem.mapIx, slotIx, loadItem.channelIx, loadItem.revision);
240 if (isValue) { assert(cacheItem.value.state == Cache::Item::State::Loading); }
241 if (isDepth) { assert(cacheItem.depth.state == Cache::Item::State::Loading); }
243 if (loadItem.failed || (loadItem.texture && loadItem.texture->hasFailedLoad())) {
244 if (isValue) { cacheItem.value.state = Cache::Item::State::Failed; }
245 if (isDepth) { cacheItem.depth.state = Cache::Item::State::Failed; }
246 LOG_TRACE(logger,
"Map=%zu Slot=%d rev=%u failed load item", loadItem.mapIx, slotIx, loadItem.revision);
250 if (loadItem.texture && loadItem.texture->isResident()) {
252 if (loadItem.texture->description.width != loadItem.texture->description.height) {
253 if (isValue) { cacheItem.value.state = Cache::Item::State::Failed; }
254 if (isDepth) { cacheItem.depth.state = Cache::Item::State::Failed; }
255 LOG_ERROR(logger,
"Map=%zu Slot=%d rev=%u texture is not square w=%u h=%u",
256 loadItem.mapIx, slotIx, loadItem.revision, loadItem.texture->description.width, loadItem.texture->description.height);
258 else if (loadItem.texture->description.width != config.
baseSize) {
259 if (isValue) { cacheItem.value.state = Cache::Item::State::Failed; }
260 if (isDepth) { cacheItem.depth.state = Cache::Item::State::Failed; }
261 LOG_ERROR(logger,
"Map=%zu Slot=%d rev=%u texture size (=%u) does not match base size (=%u)",
262 loadItem.mapIx, slotIx, loadItem.revision, loadItem.texture->description.width, config.
baseSize);
265 if (isValue) { cacheItem.value.state = Cache::Item::State::Loaded; }
266 if (isDepth) { cacheItem.depth.state = Cache::Item::State::Loaded; }
267 LOG_TRACE(logger,
"Map=%zu Slot=%d rev=%u loaded load item", loadItem.mapIx, slotIx, loadItem.revision);
268 itemsUploading.push_back(std::move(loadItem));
271 context->
engine->triggerUpdate();
274 LOG_DEBUG(logger,
"Map=%zu Slot=%d rev=%u: Cancelling loadItem isValue=%u isDepth=%u tex=%u fetch=%u",
275 loadItem.mapIx, slotIx, loadItem.revision,
276 isValue ? 1 : 0, isDepth ? 1 : 0,
277 loadItem.texture ? 1 : 0,
278 loadItem.fetchId != DataFetcherManager::NoFetchId ? 1 : 0);
281 if (isValue) { cacheItem.value.state = Cache::Item::State::None; }
282 if (isDepth) { cacheItem.depth.state = Cache::Item::State::None; }
283 if (loadItem.texture) {
284 context->textureManager->cancelTextureLoad(loadItem.texture);
287 if (loadItem.fetchId != DataFetcherManager::NoFetchId) {
288 DataFetcherManager::cancelAsyncFetch(context, loadItem.fetchId);
289 loadItem.fetchId = DataFetcherManager::NoFetchId;
294 itemsLoadingNext.push_back(std::move(loadItem));
298 LOG_TRACE(logger,
"Map=%zu Slot=%d rev=%u: Stale load item", loadItem.mapIx, slotIx, loadItem.revision);
301 if (!itemsLoading.empty() && itemsLoadingNext.empty()) {
306 itemsLoading.swap(itemsLoadingNext);
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Engine > engine
Engine instance.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ DoNotCache
Do not cache this resource for later retrieval.
@ FetchingBegin
Loading queue transitions from empty to non-empty, that is, instance needs data.
@ FetchingEnd
Loading queue transitions from non-empty to empty, that is, instance has all data it currently needs.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
@ SRGB8_JPEG
8-bit colors in SRGB color space stored as JPEG (.jpg).
@ U16_ZST
16-bit unsigned values stored as little endian raw values that are subsequently zstd compressed.
@ SRGBA8_PNG
8-bit colors in SRGB color space and a alpha channel (zero is transparent) stored as PNG (....
@ SRGB8_PNG
8-bit colors in SRGB color space stored as PNG (.png).
@ U16
16-bit unsigned values stored as little endian raw values.
uint32_t instanceId
Component instance id.
uint32_t baseSize
Base image size of a cached tile. From json.
uint8_t valueChannel
Data channel to be used as value data. From component.
uint32_t treeDepth
Depth of tile hierarchy. From json.
uint8_t depthChannel
Data channel that contains depth data. From json.
std::vector< Channel > channels
Data channels to use. From json.
bool hasDepth
If data has depth and component wants depth.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
std::vector< uint8_t > resourceData
Resource load data.
ResourceLoadFlags loadFlags
Desired loading flags. Used to specify how the resource will be loaded.