Cogs.Core
Image360System.cpp
1#include "Context.h"
2#include "ExtensionRegistry.h"
3#include "Systems/Core/TransformSystem.h"
4#include "Resources/DataFetcherManager.h"
5#include "Resources/MaterialManager.h"
6#include "Resources/TextureManager.h"
7#include "Services/Time.h"
8
9#include "Foundation/Logging/Logger.h"
10
11#include "Rendering/ICapabilities.h"
12#include "Rendering/IGraphicsDevice.h"
13
14#include "Image360System.h"
15
16namespace {
17 using namespace Cogs::Core;
18 using namespace Cogs::Core::Image360;
19
21
22 void resetComponentData(Image360::RendererExtension* renderer, Image360Data& data)
23 {
24 renderer->releaseRenderingResources(data.rendererData);
25 data = Image360Data{};
26 assert(data.state == Image360Data::State::Uninitialized);
27 }
28
29 bool lookupAndCheckForStaleness(Context* context, Image360System*& system, Image360Component*& comp, Image360Data*& data, const uint32_t instanceId)
30 {
31 system = ExtensionRegistry::getExtensionSystem<Image360System>(context);
32 for (Image360Component& comp_ : system->pool) {
33 Image360Data& data_ = system->getData(&comp_);
34 if (data_.config.instanceId == instanceId) {
35 comp = &comp_;
36 data = &data_;
37 return true;
38 }
39 }
40 LOG_DEBUG(logger, "[instance=%u] Failed to lookup instance.", instanceId);
41 return false;
42 }
43
44 void issueFetchJson(Context* context, Image360System* system, Image360Component& comp, Image360Data& data)
45 {
46 assert(data.state == Image360Data::State::Uninitialized);
47
48 data.source = comp.source;
49 if (data.source.empty()) return;
50
51 data.config.instanceId = system->instanceCounter++;
52 if (data.config.instanceId == 0) {
53 data.config.instanceId = system->instanceCounter++;
54 }
55
56 auto handler = [context, instanceId = data.config.instanceId](std::unique_ptr<Cogs::FileContents> contents)
57 {
58 auto task = [context, instanceId, contents = contents.release()]
59 {
60 Image360System* system = nullptr;
61 Image360Component* comp = nullptr;
62 Image360Data* data = nullptr;
63 if (!lookupAndCheckForStaleness(context, system, comp, data, instanceId)) {
64 LOG_WARNING(logger, "Ignoring stale json file");
65 }
66 else if(data->state != Image360Data::State::WaitingForJson) {
67 LOG_WARNING(logger, "Ignoring unexpected json file");
68 }
69 else if (!contents) {
70 LOG_ERROR(logger, "Failed to fetch json file");
71 data->state = Image360Data::State::Error;
72 }
73 else if (!data->config.parseConfigJson(contents)) {
74 LOG_ERROR(logger, "Failed to parse json file (source=%.*s)", StringViewFormat(contents->origin()));
75 data->state = Image360Data::State::Error;
76 }
77 else {
78 LOG_DEBUG(logger, "json file parsed successfully (source=%.*s)", StringViewFormat(contents->origin()));
79 data->state = Image360Data::State::JsonParsed;
80 }
81 delete contents;
82 context->engine->setDirty();
83 };
84 context->engine->runTaskInMainThread(std::move(task));
85 };
86
87 LOG_DEBUG(logger, "Fetching json %s", data.source.c_str());
88 data.state = Image360Data::State::WaitingForJson;
89 DataFetcherManager::fetchAsync(context, data.source, handler);
90 }
91
92}
93
94Cogs::Core::Image360System::~Image360System()
95{
96 assert(!renderer);
97 assert(!bounds);
98}
99
101{
102 if (pool.size() == 0) {
103
104 if (material == MaterialHandle::NoHandle) {
105 material = context->materialManager->loadMaterial("Materials/Image360.material");
106 context->materialManager->processLoading();
107 }
108
109 assert(!renderer);
110 renderer = new Image360::RendererExtension();
111 context->renderer->registerExtension(renderer);
112
113 assert(!bounds);
114 bounds = new Image360::Bounds(this);
115 context->bounds->addBoundsExtension(bounds);
116
117 assert(!picker);
118 picker = new Image360::RayPickExtension(this);
119 context->rayPicking->addPickable(picker);
120
121 LOG_DEBUG(logger, "First component created, registered image 360 renderer, bounds and raypick extensions");
122 }
123
125
126 return handle;
127}
128
130{
131 Image360Component* im360Comp = static_cast<Image360Component*>(handle.resolve());
132 Image360Data& im360Data = getData(im360Comp);
133 resetComponentData( renderer, im360Data);
134
136 if (pool.size() == 0) {
137
138 assert(renderer);
139 renderer->release();
140 context->renderer->unregisterExtension(renderer);
141 delete renderer;
142 renderer = nullptr;
143
144 assert(bounds);
145 context->bounds->removeBoundsExtension(bounds);
146 delete bounds;
147 bounds = nullptr;
148
149 assert(picker);
150 context->rayPicking->removePickable(picker);
151 delete picker;
152 picker = nullptr;
153
154 LOG_DEBUG(logger, "Last component destroyed, unregistered image 360 renderer, bounds and raypick extensions");
155 }
156}
157
158
160{
161 base::initialize(context);
162}
163
165{
166 CpuInstrumentationScope(SCOPE_POTREE, "Image360System::update");
167
169
170
171 // Limit due to we use the positive part of uin16 for indexing, where the lower
172 // three bits are used to offset into parent textures.
173 uint32_t cacheMaxCount = 0x8000u >> 3u;
174
175 if (const Variable* var = context->variables->get("image360.cacheMaxCount"); !var->isEmpty()) {
176 cacheMaxCount = std::min(cacheMaxCount, uint32_t(std::max(6, var->getInt())));
177 }
178 else {
179 context->variables->set("image360.cacheMaxCount", int(cacheMaxCount));
180 }
181
182 uint32_t treeMaxSize = caps.MaxTexture2DSize;
183 if (const Variable* var = context->variables->get("image360.treeMaxSize"); !var->isEmpty()) {
184 treeMaxSize = std::min(treeMaxSize, uint32_t(std::max(6, var->getInt())));
185 }
186 else {
187 context->variables->set("image360.treeMaxSize", int(treeMaxSize));
188 }
189
190 uint32_t maxConcurrent = 2;
191 if (const Variable* var = context->variables->get("image360.maxConcurrentRequests"); !var->isEmpty()) {
192 maxConcurrent = uint32_t(std::max(1, var->getInt()));
193 }
194 else {
195 context->variables->set("image360.maxConcurrentRequests", int(maxConcurrent));
196 }
197
198
199 for (Image360Component& im360Comp : pool) {
200
201 Image360Data& im360Data = getData(&im360Comp);
202 if (im360Data.config.baseSize) {
203 im360Data.config.cacheMaxCount = std::min(cacheMaxCount, caps.MaxTextureArrayLayers);
204 im360Data.config.treeMaxSize = treeMaxSize;
205 }
206 else {
207 im360Data.config.cacheMaxCount = 0;
208 im360Data.config.treeMaxSize = 0;
209 }
210 im360Data.config.maxConcurrent = maxConcurrent;
211
212 if (!im360Comp.isVisible()) {
213 if (im360Data.state != Image360Data::State::Uninitialized) {
214 resetComponentData(renderer, im360Data);
215 assert(im360Data.state == Image360Data::State::Uninitialized);
216 }
217 continue;
218 }
219
220 if ((im360Data.valueChannel != im360Comp.valueChannel) ||
221 (im360Data.hasDepth != im360Comp.hasDepth) ||
222 (im360Data.source != im360Comp.source))
223 {
224 if (im360Data.state != Image360Data::State::Uninitialized) {
225 resetComponentData(renderer, im360Data);
226 assert(im360Data.state == Image360Data::State::Uninitialized);
227 }
228 im360Data.valueChannel = im360Comp.valueChannel;
229 im360Data.hasDepth = im360Comp.hasDepth;
230 im360Data.source = im360Comp.source;
231 issueFetchJson(context, this, im360Comp, im360Data);
232 continue;
233 }
234
235 if (im360Comp.hasChanged()) {
236
237 if (HandleIsValid(im360Data.materialInstance)) {
238 im360Data.materialInstance->setVariant("DebugColors", im360Comp.debugColors);
239 im360Data.materialInstance->setVariant("DebugDepth", im360Comp.debugDepth);
240 }
241 }
242
243 const uint32_t currentFrame = context->time->getFrame();
244 im360Data.fetcher.processLoadItems(context, im360Comp, im360Data.cache, im360Data.config, currentFrame);
245
246 if (im360Data.state == Image360Data::State::JsonParsed) {
247
248 im360Data.materialInstance = context->materialInstanceManager->createMaterialInstance(material);
249 im360Data.materialInstance->setVariant("DebugColors", im360Comp.debugColors);
250 im360Data.materialInstance->setVariant("TreeDepth", int(im360Data.config.treeDepth));
251
252 im360Data.materialInstance->setVariant("HasGradient", HandleIsValid(im360Comp.gradient));
253 im360Data.materialInstance->setTextureProperty("gradient", im360Comp.gradient);
254
255 im360Data.config.valueChannel = im360Comp.valueChannel < im360Data.config.channels.size() ? uint8_t(im360Comp.valueChannel) : 0;
256 switch (im360Data.config.channels[im360Data.config.valueChannel].dataType)
257 {
258 case Config::Channel::DataType::SRGB8_JPEG: [[fallthrough]];
259 case Config::Channel::DataType::SRGB8_PNG:
260 im360Data.materialInstance->setVariant("ValueType", "srgb");
261 break;
262 case Config::Channel::DataType::SRGBA8_PNG:
263 im360Data.materialInstance->setVariant("ValueType", "srgb");
264 break;
265 case Config::Channel::DataType::U16: [[fallthrough]];
266 case Config::Channel::DataType::U16_ZST:
267 im360Data.materialInstance->setVariant("ValueType", "uint");
268 break;
269 default:
270 break;
271 }
272
273 im360Data.config.hasDepth = im360Comp.hasDepth && im360Data.config.depthChannel < im360Data.config.channels.size();
274 if (im360Data.config.hasDepth) {
275 im360Data.materialInstance->options.depthTestEnabled = true;
276 im360Data.materialInstance->setVariant("HasDepth", true);
277 im360Data.materialInstance->setVariant("DiscardNoDepth", im360Data.config.discardNoDepth);
278 im360Data.materialInstance->setVariant("DebugDepth", im360Comp.debugDepth);
279 }
280 else {
281 im360Data.materialInstance->options.depthTestEnabled = false;
282 im360Data.materialInstance->setVariant("HasDepth", false);
283 }
284
285 im360Data.cache.init(im360Data.config);
286 im360Data.state = Image360Data::State::WaitingForBaseLevel;
287 }
288
289 if (im360Data.state == Image360Data::State::WaitingForBaseLevel) {
290 bool ready = true;
291 for (size_t i = 0; i < 6; i++) {
292 if (im360Data.cache.isQuadInCache(context, im360Data.fetcher, im360Comp, im360Data.config, currentFrame, 0, i) < 0) {
293 ready = false;
294 }
295 }
296 if (ready) {
297 LOG_DEBUG(logger, "Base level in cache");
298 context->engine->invokeComponentNotifyCallback(im360Comp, (int)Image360Notification::Ready, nullptr, 0);
299 im360Data.state = Image360Data::State::Running;
300 }
301 }
302
303
304 if (im360Data.state == Image360Data::State::Running && !im360Comp.disableLodUpdates) {
305 im360Data.lodTree.update(context, im360Data.rendererData, im360Data.fetcher, im360Data.cache, im360Comp, im360Data.config, currentFrame, im360Comp);
306 im360Data.cache.updateLeastRecentlyUsedList(currentFrame);
307 }
308 }
309}
virtual ComponentHandle createComponent()
Create a new component instance.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
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.
Definition: Context.h:83
class IRenderer * renderer
Renderer.
Definition: Context.h:228
std::unique_ptr< class Bounds > bounds
Bounds service instance.
Definition: Context.h:216
std::unique_ptr< class RayPicking > rayPicking
RayPicking service instance.
Definition: Context.h:213
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
std::unique_ptr< class Time > time
Time service instance.
Definition: Context.h:198
std::unique_ptr< class Engine > engine
Engine instance.
Definition: Context.h:222
virtual IGraphicsDevice * getDevice()=0
Get the graphics device used by the renderer.
virtual void registerExtension(IRendererExtension *extension)=0
Register an extension with the renderer.
virtual void unregisterExtension(IRendererExtension *extension)=0
Unregister an extension with the renderer.
virtual ICapabilities * getCapabilities()=0
Get a pointer to the capability management interface used to query the graphics device capability fla...
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ Ready
All 6 baselayers are fully loaded, instance will begin rendering.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Handle to a Component instance.
Definition: Component.h:67
COGSFOUNDATION_API class Component * resolve() const
Resolve the handle, returning a pointer to the held Component instance.
Definition: Component.cpp:65
std::string source
URL of json describing image pyramid.
void initialize(Context *context) override
Initialize the system.
ComponentHandle createComponent() override
void destroyComponent(ComponentHandle component) override
uint32_t instanceId
Component instance id.
Definition: Image360.h:44
uint32_t baseSize
Base image size of a cached tile. From json.
Definition: Image360.h:46
uint8_t valueChannel
Data channel to be used as value data. From component.
Definition: Image360.h:49
uint32_t treeDepth
Depth of tile hierarchy. From json.
Definition: Image360.h:45
uint8_t depthChannel
Data channel that contains depth data. From json.
Definition: Image360.h:50
std::vector< Channel > channels
Data channels to use. From json.
Definition: Image360.h:51
bool discardNoDepth
Discard pixels with no depth data.
Definition: Image360.h:59
bool hasDepth
If data has depth and component wants depth.
Definition: Image360.h:58
void setTextureProperty(const StringView &key, TextureHandle value)
Set the texture property with the given key to the texture resource held by value.
MaterialOptions options
Material rendering options used by this instance.
bool depthTestEnabled
If depth testing should be enabled rendering the geometry with the material instance.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
Runtime control variable.
Definition: Variables.h:27
Contains device capabilities.
Definition: ICapabilities.h:67
uint32_t MaxTexture2DSize
Using D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION as default.
Definition: ICapabilities.h:80
uint32_t MaxTextureArrayLayers
Using D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION as default.
Definition: ICapabilities.h:83
virtual const GraphicsDeviceCapabilities & getDeviceCapabilities() const
Gets the device capabilities in a structure.