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