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