1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "engine.h"
17 
18 #include <algorithm>
19 #include <chrono>
20 #include <cstring>
21 
22 #include <base/containers/array_view.h>
23 #include <base/containers/iterator.h>
24 #include <base/containers/refcnt_ptr.h>
25 #include <base/containers/string.h>
26 #include <base/containers/string_view.h>
27 #include <base/containers/type_traits.h>
28 #include <base/containers/unique_ptr.h>
29 #include <base/containers/unordered_map.h>
30 #include <base/containers/vector.h>
31 #include <base/namespace.h>
32 #include <base/util/uid.h>
33 #include <core/ecs/intf_ecs.h>
34 #include <core/engine_info.h>
35 #include <core/implementation_uids.h>
36 #include <core/intf_engine.h>
37 #include <core/io/intf_file_manager.h>
38 #include <core/io/intf_filesystem_api.h>
39 #include <core/log.h>
40 #include <core/namespace.h>
41 #include <core/os/intf_platform.h>
42 #include <core/plugin/intf_class_factory.h>
43 #include <core/plugin/intf_class_register.h>
44 #include <core/plugin/intf_interface.h>
45 #include <core/plugin/intf_plugin.h>
46 #include <core/plugin/intf_plugin_register.h>
47 #include <core/threading/intf_thread_pool.h>
48 
49 #include "image/image_loader_manager.h"
50 #include "image/loaders/image_loader_ktx.h"
51 #include "image/loaders/image_loader_stb_image.h"
52 #include "os/platform.h"
53 
54 #if (CORE_PERF_ENABLED == 1)
55 #include "perf/performance_data_manager.h"
56 #endif
57 
58 CORE_BEGIN_NAMESPACE()
59 namespace {
60 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
61 // Core Rofs Data.
62 extern "C" const uint64_t SIZEOFDATAFORCORE;
63 extern "C" const void* const BINARYDATAFORCORE[];
64 #endif
65 
66 using BASE_NS::array_view;
67 using BASE_NS::make_unique;
68 using BASE_NS::pair;
69 using BASE_NS::string;
70 using BASE_NS::string_view;
71 using BASE_NS::Uid;
72 
73 // This is defined in the CMake generated version.cpp
LogEngineBuildInfo()74 void LogEngineBuildInfo()
75 {
76 #define CORE_TO_STRING_INTERNAL(x) #x
77 #define CORE_TO_STRING(x) CORE_TO_STRING_INTERNAL(x)
78 
79 #ifdef NDEBUG
80     CORE_LOG_I("Core engine version: %s", GetVersion().data());
81 #else
82     CORE_LOG_I("Version: %s (DEBUG)", GetVersion().data());
83 #endif
84 
85     CORE_LOG_I("CORE_VALIDATION_ENABLED=" CORE_TO_STRING(CORE_VALIDATION_ENABLED));
86     CORE_LOG_I("CORE_DEV_ENABLED=" CORE_TO_STRING(CORE_DEV_ENABLED));
87 }
88 } // namespace
89 
Engine(EngineCreateInfo const & createInfo)90 Engine::Engine(EngineCreateInfo const& createInfo)
91     : platform_(Platform::Create(createInfo.platformCreateInfo)), applicationContext_(createInfo.applicationContext)
92 {
93     LogEngineBuildInfo();
94     auto factory = CORE_NS::GetInstance<IFileSystemApi>(UID_FILESYSTEM_API_FACTORY);
95     fileManager_ = factory->CreateFilemanager();
96     fileManager_->RegisterFilesystem("file", factory->CreateStdFileSystem());
97     fileManager_->RegisterFilesystem("memory", factory->CreateMemFileSystem());
98 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
99     fileManager_->RegisterFilesystem(
100         "corerofs", factory->CreateROFilesystem(BINARYDATAFORCORE, static_cast<size_t>(SIZEOFDATAFORCORE)));
101 #endif
102 
103     RegisterDefaultPaths();
104 }
105 
~Engine()106 Engine::~Engine()
107 {
108     GetPluginRegister().RemoveListener(*this);
109 
110 #if (CORE_PERF_ENABLED == 1)
111     if (auto perfFactory = CORE_NS::GetInstance<IPerformanceDataManagerFactory>(UID_PERFORMANCE_FACTORY); perfFactory) {
112         for (const auto& perfMan : perfFactory->GetAllCategories()) {
113             CORE_LOG_I("%s PerformanceData for this run:", perfMan->GetCategory().data());
114             static_cast<const PerformanceDataManager&>(*perfMan).DumpToLog();
115         }
116     }
117 #endif
118 
119     UnloadPlugins();
120 
121     fileManager_.reset();
122 }
123 
124 CORE_NS::IEcs* IEcsInstance(IClassFactory&, const IThreadPool::Ptr&);
125 
CreateEcs()126 IEcs::Ptr Engine::CreateEcs()
127 {
128     // construct a secondary ecs instance.
129     if (auto threadFactory = CORE_NS::GetInstance<ITaskQueueFactory>(UID_TASK_QUEUE_FACTORY); threadFactory) {
130         auto threadPool = threadFactory->CreateThreadPool(threadFactory->GetNumberOfCores());
131         return IEcs::Ptr { IEcsInstance(*this, threadPool) };
132     }
133 
134     return IEcs::Ptr {};
135 }
136 
CreateEcs(IThreadPool & threadPool)137 IEcs::Ptr Engine::CreateEcs(IThreadPool& threadPool)
138 {
139     return IEcs::Ptr { IEcsInstance(*this, IThreadPool::Ptr { &threadPool }) };
140 }
141 
Init()142 void Engine::Init()
143 {
144     CORE_LOG_D("Engine init.");
145 
146     imageManager_ = make_unique<ImageLoaderManager>(*fileManager_);
147 
148     // Pre-register some basic image formats.
149 
150     LoadPlugins();
151 
152     GetPluginRegister().AddListener(*this);
153 }
154 
RegisterDefaultPaths()155 void Engine::RegisterDefaultPaths()
156 {
157     platform_->RegisterDefaultPaths(*fileManager_);
158 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
159     // Create engine:// protocol that points to embedded engine asset files.
160     CORE_LOG_D("Registered core asset path: 'corerofs://core/'");
161     fileManager_->RegisterPath("engine", "corerofs://core/", false);
162 #endif
163 
164     // Create shaders:// protocol that points to shader files.
165     fileManager_->RegisterPath("shaders", "engine://shaders/", false);
166     // Create shaderstates:// protocol that points to (graphics) shader state files.
167     fileManager_->RegisterPath("shaderstates", "engine://shaderstates/", false);
168     // Create vertexinputdeclarations:// protocol that points to vid files.
169     fileManager_->RegisterPath("vertexinputdeclarations", "engine://vertexinputdeclarations/", false);
170     // Create pipelinelayouts:// protocol that points to vid files.
171     fileManager_->RegisterPath("pipelinelayouts", "engine://pipelinelayouts/", false);
172     // Create renderdataconfigurations:// protocol that points to render byteData configurations.
173     fileManager_->RegisterPath("renderdataconfigurations", "engine://renderdataconfigurations/", false);
174 }
175 
UnloadPlugins()176 void Engine::UnloadPlugins()
177 {
178     for (auto& plugin : plugins_) {
179         if (plugin.second->destroyPlugin) {
180             plugin.second->destroyPlugin(plugin.first);
181         }
182     }
183 }
184 
LoadPlugins()185 void Engine::LoadPlugins()
186 {
187     for (auto info : CORE_NS::GetPluginRegister().GetTypeInfos(IEnginePlugin::UID)) {
188         if (auto enginePlugin = static_cast<const IEnginePlugin*>(info); enginePlugin && enginePlugin->createPlugin) {
189             auto token = enginePlugin->createPlugin(*this);
190             plugins_.push_back({ token, enginePlugin });
191         }
192     }
193 }
194 
TickFrame()195 bool Engine::TickFrame()
196 {
197     return TickFrame({ nullptr, size_t(0) });
198 }
199 
TickFrame(const array_view<IEcs * > & ecsInputs)200 bool Engine::TickFrame(const array_view<IEcs*>& ecsInputs)
201 {
202     using namespace std::chrono;
203     const auto currentTime =
204         static_cast<uint64_t>(duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count());
205 
206     if (firstTime_ == ~0u) {
207         previousFrameTime_ = firstTime_ = currentTime;
208     }
209     deltaTime_ = currentTime - previousFrameTime_;
210     constexpr auto limitHz = duration_cast<microseconds>(duration<float, std::ratio<1, 15u>>(1)).count();
211     if (deltaTime_ > limitHz) {
212         deltaTime_ = limitHz; // clamp the time step to no longer than 15hz.
213     }
214     previousFrameTime_ = currentTime;
215 
216     const uint64_t totalTime = currentTime - firstTime_;
217 
218     bool needRender = false;
219     for (auto& ecs : ecsInputs) {
220         if (TickFrame(*ecs, totalTime, deltaTime_)) {
221             needRender = true;
222         }
223     }
224 
225     return needRender;
226 }
227 
TickFrame(IEcs & ecs,uint64_t totalTime,uint64_t deltaTime)228 bool Engine::TickFrame(IEcs& ecs, uint64_t totalTime, uint64_t deltaTime)
229 {
230     // run garbage collection before updating the systems to ensure only valid entities/ components are available.
231     ecs.ProcessEvents();
232 
233     const bool needRender = ecs.Update(totalTime, deltaTime);
234 
235     // do gc also after the systems have been updated to ensure any deletes done by systems are effective
236     // and client doesn't see stale entities.
237     ecs.ProcessEvents();
238 
239     return needRender;
240 }
241 
GetImageLoaderManager()242 IImageLoaderManager& Engine::GetImageLoaderManager()
243 {
244     CORE_ASSERT_MSG(imageManager_, "Engine not initialized");
245     return *imageManager_;
246 }
247 
GetFileManager()248 IFileManager& Engine::GetFileManager()
249 {
250     CORE_ASSERT_MSG(fileManager_, "Engine not initialized");
251     return *fileManager_;
252 }
253 
GetEngineTime() const254 EngineTime Engine::GetEngineTime() const
255 {
256     return { previousFrameTime_ - firstTime_, deltaTime_ };
257 }
258 
GetInterface(const Uid & uid) const259 const IInterface* Engine::GetInterface(const Uid& uid) const
260 {
261     if ((uid == IEngine::UID) || (uid == IClassFactory::UID) || (uid == IInterface::UID)) {
262         return static_cast<const IEngine*>(this);
263     }
264     if (uid == IClassRegister::UID) {
265         return static_cast<const IClassRegister*>(this);
266     }
267 
268     return nullptr;
269 }
270 
GetInterface(const Uid & uid)271 IInterface* Engine::GetInterface(const Uid& uid)
272 {
273     if ((uid == IEngine::UID) || (uid == IClassFactory::UID) || (uid == IInterface::UID)) {
274         return static_cast<IEngine*>(this);
275     }
276     if (uid == IClassRegister::UID) {
277         return static_cast<IClassRegister*>(this);
278     }
279 
280     return nullptr;
281 }
282 
RegisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)283 void Engine::RegisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
284 {
285     // keep interfaceTypeInfos_ sorted according to UIDs
286     const auto pos = std::upper_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
287         [](Uid value, const InterfaceTypeInfo* element) { return value < element->uid; });
288     interfaceTypeInfos_.insert(pos, &interfaceInfo);
289 }
290 
UnregisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)291 void Engine::UnregisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
292 {
293     if (!interfaceTypeInfos_.empty()) {
294         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
295             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
296         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == interfaceInfo.uid) {
297             interfaceTypeInfos_.erase(pos);
298         }
299     }
300 }
301 
GetInterfaceMetadata() const302 array_view<const InterfaceTypeInfo* const> Engine::GetInterfaceMetadata() const
303 {
304     return interfaceTypeInfos_;
305 }
306 
GetInterfaceMetadata(const Uid & uid) const307 const InterfaceTypeInfo& Engine::GetInterfaceMetadata(const Uid& uid) const
308 {
309     static constexpr InterfaceTypeInfo invalidType {};
310 
311     if (!interfaceTypeInfos_.empty()) {
312         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), uid,
313             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
314         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == uid) {
315             return *(*pos);
316         }
317     }
318     return invalidType;
319 }
320 
GetInstance(const Uid & uid) const321 IInterface* Engine::GetInstance(const Uid& uid) const
322 {
323     const auto& data = GetInterfaceMetadata(uid);
324     if (data.getInterface) {
325         return data.getInterface(const_cast<Engine&>(*this), data.token);
326     }
327     return nullptr;
328 }
329 
CreateInstance(const Uid & uid)330 IInterface::Ptr Engine::CreateInstance(const Uid& uid)
331 {
332     const auto& data = GetInterfaceMetadata(uid);
333     if (data.createInterface) {
334         return IInterface::Ptr { data.createInterface(*this, data.token) };
335     }
336     return {};
337 }
338 
OnTypeInfoEvent(EventType type,array_view<const ITypeInfo * const> typeInfos)339 void Engine::OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos)
340 {
341     if (type == EventType::ADDED) {
342         for (const auto* info : typeInfos) {
343             if (info && info->typeUid == IEnginePlugin::UID && static_cast<const IEnginePlugin*>(info)->createPlugin) {
344                 auto enginePlugin = static_cast<const IEnginePlugin*>(info);
345                 if (std::none_of(plugins_.begin(), plugins_.end(),
346                         [enginePlugin](const pair<PluginToken, const IEnginePlugin*>& pluginData) {
347                             return pluginData.second == enginePlugin;
348                         })) {
349                     auto token = enginePlugin->createPlugin(*this);
350                     plugins_.push_back({ token, enginePlugin });
351                 }
352             }
353         }
354     } else if (type == EventType::REMOVED) {
355         for (const auto* info : typeInfos) {
356             if (info && info->typeUid == IEnginePlugin::UID) {
357                 auto enginePlugin = static_cast<const IEnginePlugin*>(info);
358                 if (auto pos = std::find_if(plugins_.cbegin(), plugins_.cend(),
359                         [enginePlugin](const pair<PluginToken, const IEnginePlugin*>& pluginData) {
360                             return pluginData.second == enginePlugin;
361                         });
362                     pos != plugins_.cend()) {
363                     if (enginePlugin->destroyPlugin) {
364                         enginePlugin->destroyPlugin(pos->first);
365                     }
366                     plugins_.erase(pos);
367                 }
368             }
369         }
370     }
371 }
372 
GetVersion()373 string_view Engine::GetVersion()
374 {
375     return CORE_NS::GetVersion();
376 }
377 
IsDebugBuild()378 bool Engine::IsDebugBuild()
379 {
380     return CORE_NS::IsDebugBuild();
381 }
382 
CreateEngine(EngineCreateInfo const & createInfo)383 IEngine::Ptr CreateEngine(EngineCreateInfo const& createInfo)
384 {
385     auto engine = new Engine(createInfo);
386     return IEngine::Ptr { engine };
387 }
388 
GetPlatform() const389 const IPlatform& Engine::GetPlatform() const
390 {
391     return *platform_;
392 }
393 
Ref()394 void Engine::Ref()
395 {
396     refCount_++;
397 }
398 
Unref()399 void Engine::Unref()
400 {
401     if (--refCount_ == 0) {
402         delete this;
403     }
404 }
405 CORE_END_NAMESPACE()
406