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 im360Data.rendererData.isValueUint = false;
262 break;
263 case Config::Channel::DataType::SRGBA8_PNG:
264 im360Data.materialInstance->setVariant("ValueType", "srgb");
265 im360Data.rendererData.isValueUint = false;
266 break;
267 case Config::Channel::DataType::U16: [[fallthrough]];
268 case Config::Channel::DataType::U16_ZST:
269 im360Data.materialInstance->setVariant("ValueType", "uint");
270 im360Data.rendererData.isValueUint = true;
271 break;
272 default:
273 break;
274 }
275
276 im360Data.config.hasDepth = im360Comp.hasDepth && im360Data.config.depthChannel < im360Data.config.channels.size();
277 if (im360Data.config.hasDepth) {
278 im360Data.materialInstance->options.depthTestEnabled = true;
279 im360Data.materialInstance->setVariant("HasDepth", true);
280 im360Data.materialInstance->setVariant("DiscardNoDepth", im360Data.config.discardNoDepth);
281 im360Data.materialInstance->setVariant("DebugDepth", im360Comp.debugDepth);
282 }
283 else {
284 im360Data.materialInstance->options.depthTestEnabled = false;
285 im360Data.materialInstance->setVariant("HasDepth", false);
286 }
287
288 im360Data.cache.init(im360Data.config);
289 im360Data.state = Image360Data::State::WaitingForBaseLevel;
290 }
291
292 if (im360Data.state == Image360Data::State::WaitingForBaseLevel) {
293 bool ready = true;
294 for (size_t i = 0; i < 6; i++) {
295 if (im360Data.cache.isQuadInCache(context, im360Data.fetcher, im360Comp, im360Data.config, currentFrame, 0, i) < 0) {
296 ready = false;
297 }
298 }
299 if (ready) {
300 LOG_DEBUG(logger, "Base level in cache");
301 context->engine->invokeComponentNotifyCallback(im360Comp, (int)Image360Notification::Ready, nullptr, 0);
302 im360Data.state = Image360Data::State::Running;
303 }
304 }
305
306
307 if (im360Data.state == Image360Data::State::Running && !im360Comp.disableLodUpdates) {
308 im360Data.lodTree.update(context, im360Data.rendererData, im360Data.fetcher, im360Data.cache, im360Comp, im360Data.config, currentFrame, im360Comp);
309 im360Data.cache.updateLeastRecentlyUsedList(currentFrame);
310 }
311 }
312}
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:140
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:181
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.