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/namespace.h>
31 #include <base/util/errors.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 "os/platform.h"
51
52 #if (CORE_PERF_ENABLED == 1)
53 #include "perf/performance_data_manager.h"
54 #endif
55
56 CORE_BEGIN_NAMESPACE()
57 namespace {
58 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
59 // Core Rofs Data.
60 extern "C" const uint64_t SIZEOFDATAFORCORE;
61 extern "C" const void* const BINARYDATAFORCORE[];
62 #endif
63
64 using BASE_NS::array_view;
65 using BASE_NS::make_unique;
66 using BASE_NS::pair;
67 using BASE_NS::string;
68 using BASE_NS::string_view;
69 using BASE_NS::Uid;
70
71 // This is defined in the CMake generated version.cpp
LogEngineBuildInfo()72 void LogEngineBuildInfo()
73 {
74 #define CORE_TO_STRING_INTERNAL(x) #x
75 #define CORE_TO_STRING(x) CORE_TO_STRING_INTERNAL(x)
76
77 #ifdef NDEBUG
78 CORE_LOG_I("Core engine version: %s", GetVersion().data());
79 #else
80 CORE_LOG_I("Version: %s (DEBUG)", GetVersion().data());
81 #endif
82
83 CORE_LOG_I("CORE_VALIDATION_ENABLED=" CORE_TO_STRING(CORE_VALIDATION_ENABLED));
84 CORE_LOG_I("CORE_DEV_ENABLED=" CORE_TO_STRING(CORE_DEV_ENABLED));
85 CORE_LOG_I("CORE_PERF_ENABLED=" CORE_TO_STRING(CORE_PERF_ENABLED));
86 }
87
GetThreadPoolThreadCount(const uint32_t numberOfHwCores)88 inline constexpr uint32_t GetThreadPoolThreadCount(const uint32_t numberOfHwCores)
89 {
90 // reduce the thread count for default ECS thread pool
91 return numberOfHwCores / 2U;
92 }
93 } // namespace
94
WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)95 WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)
96 Engine::Engine(EngineCreateInfo const& createInfo)
97 : platform_(Platform::Create(createInfo.platformCreateInfo)), resourceManager_(*this)
98 {
99 LogEngineBuildInfo();
100 auto factory = CORE_NS::GetInstance<IFileSystemApi>(UID_FILESYSTEM_API_FACTORY);
101 fileManager_ = factory->CreateFilemanager();
102 fileManager_->RegisterFilesystem("file", factory->CreateStdFileSystem());
103 fileManager_->RegisterFilesystem("memory", factory->CreateMemFileSystem());
104 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
105 fileManager_->RegisterFilesystem(
106 "corerofs", factory->CreateROFilesystem(BINARYDATAFORCORE, static_cast<size_t>(SIZEOFDATAFORCORE)));
107 #endif
108
109 RegisterDefaultPaths();
110 }
WARNING_SCOPE_END()111 WARNING_SCOPE_END()
112
113 Engine::~Engine()
114 {
115 GetPluginRegister().RemoveListener(*this);
116
117 #if (CORE_PERF_ENABLED == 1)
118 if (auto perfFactory = CORE_NS::GetInstance<IPerformanceDataManagerFactory>(UID_PERFORMANCE_FACTORY); perfFactory) {
119 for (const auto& perfMan : perfFactory->GetAllCategories()) {
120 CORE_LOG_I("%s PerformanceData for this run:", perfMan->GetCategory().data());
121 static_cast<const PerformanceDataManager&>(*perfMan).DumpToLog();
122 }
123 }
124 #endif
125
126 UnloadPlugins();
127
128 fileManager_.reset();
129 }
130
131 CORE_NS::IEcs* IEcsInstance(IClassFactory&, const IThreadPool::Ptr&);
132
CreateEcs()133 IEcs::Ptr Engine::CreateEcs()
134 {
135 // construct a secondary ecs instance.
136 if (auto threadFactory = CORE_NS::GetInstance<ITaskQueueFactory>(UID_TASK_QUEUE_FACTORY); threadFactory) {
137 auto threadPool = threadFactory->CreateThreadPool(GetThreadPoolThreadCount(threadFactory->GetNumberOfCores()));
138 return IEcs::Ptr { IEcsInstance(*this, threadPool) };
139 }
140
141 return IEcs::Ptr {};
142 }
143
CreateEcs(IThreadPool & threadPool)144 IEcs::Ptr Engine::CreateEcs(IThreadPool& threadPool)
145 {
146 return IEcs::Ptr { IEcsInstance(*this, IThreadPool::Ptr { &threadPool }) };
147 }
148
Init()149 void Engine::Init()
150 {
151 CORE_LOG_D("Engine init.");
152
153 imageManager_ = make_unique<ImageLoaderManager>(*fileManager_);
154
155 LoadPlugins();
156
157 GetPluginRegister().AddListener(*this);
158 }
159
RegisterDefaultPaths()160 void Engine::RegisterDefaultPaths()
161 {
162 #if (CORE_EMBEDDED_ASSETS_ENABLED == 1)
163 // Create engine:// protocol that points to embedded engine asset files.
164 CORE_LOG_D("Registered core asset path: 'corerofs://core/'");
165 fileManager_->RegisterPath("engine", "corerofs://core/", false);
166 #endif
167
168 // Create shaders:// protocol that points to shader files.
169 fileManager_->RegisterPath("shaders", "engine://shaders/", false);
170 // Create shaderstates:// protocol that points to (graphics) shader state files.
171 fileManager_->RegisterPath("shaderstates", "engine://shaderstates/", false);
172 // Create vertexinputdeclarations:// protocol that points to vid files.
173 fileManager_->RegisterPath("vertexinputdeclarations", "engine://vertexinputdeclarations/", false);
174 // Create pipelinelayouts:// protocol that points to vid files.
175 fileManager_->RegisterPath("pipelinelayouts", "engine://pipelinelayouts/", false);
176 // Create renderdataconfigurations:// protocol that points to render byteData configurations.
177 fileManager_->RegisterPath("renderdataconfigurations", "engine://renderdataconfigurations/", false);
178 }
179
UnloadPlugins()180 void Engine::UnloadPlugins()
181 {
182 for (auto& plugin : plugins_) {
183 if (plugin.second->destroyPlugin) {
184 plugin.second->destroyPlugin(plugin.first);
185 }
186 }
187 }
188
LoadPlugins()189 void Engine::LoadPlugins()
190 {
191 for (auto info : CORE_NS::GetPluginRegister().GetTypeInfos(IEnginePlugin::UID)) {
192 if (auto enginePlugin = static_cast<const IEnginePlugin*>(info); enginePlugin && enginePlugin->createPlugin) {
193 auto token = enginePlugin->createPlugin(*this);
194 plugins_.push_back({ token, enginePlugin });
195 }
196 }
197 }
198
TickFrame()199 bool Engine::TickFrame()
200 {
201 return TickFrame({ nullptr, size_t(0) });
202 }
203
TickFrame(const array_view<IEcs * > & ecsInputs)204 bool Engine::TickFrame(const array_view<IEcs*>& ecsInputs)
205 {
206 using namespace std::chrono;
207 const auto currentTime =
208 static_cast<uint64_t>(duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count());
209
210 if (firstTime_ == ~0u) {
211 previousFrameTime_ = firstTime_ = currentTime;
212 }
213 deltaTime_ = currentTime - previousFrameTime_;
214 constexpr auto limitHz = duration_cast<microseconds>(duration<float, std::ratio<1, 15u>>(1)).count();
215 if (deltaTime_ > limitHz) {
216 deltaTime_ = limitHz; // clamp the time step to no longer than 15hz.
217 }
218 previousFrameTime_ = currentTime;
219
220 const uint64_t totalTime = currentTime - firstTime_;
221
222 bool needRender = false;
223 for (auto& ecs : ecsInputs) {
224 if (TickFrame(*ecs, totalTime, deltaTime_)) {
225 needRender = true;
226 }
227 }
228
229 return needRender;
230 }
231
TickFrame(IEcs & ecs,uint64_t totalTime,uint64_t deltaTime)232 bool Engine::TickFrame(IEcs& ecs, uint64_t totalTime, uint64_t deltaTime)
233 {
234 // run garbage collection before updating the systems to ensure only valid entities/ components are available.
235 ecs.ProcessEvents();
236
237 const bool needRender = ecs.Update(totalTime, deltaTime);
238
239 // do gc also after the systems have been updated to ensure any deletes done by systems are effective
240 // and client doesn't see stale entities.
241 ecs.ProcessEvents();
242
243 return needRender;
244 }
245
GetImageLoaderManager()246 IImageLoaderManager& Engine::GetImageLoaderManager()
247 {
248 CORE_ASSERT_MSG(imageManager_, "Engine not initialized");
249 return *imageManager_;
250 }
251
GetFileManager()252 IFileManager& Engine::GetFileManager()
253 {
254 CORE_ASSERT_MSG(fileManager_, "Engine not initialized");
255 return *fileManager_;
256 }
257
GetEngineTime() const258 EngineTime Engine::GetEngineTime() const
259 {
260 return { previousFrameTime_ - firstTime_, deltaTime_ };
261 }
262
RegisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)263 void Engine::RegisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
264 {
265 // keep interfaceTypeInfos_ sorted according to UIDs
266 const auto pos = std::upper_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
267 [](Uid value, const InterfaceTypeInfo* element) { return value < element->uid; });
268 interfaceTypeInfos_.insert(pos, &interfaceInfo);
269 }
270
UnregisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)271 void Engine::UnregisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
272 {
273 if (!interfaceTypeInfos_.empty()) {
274 const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
275 [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
276 if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == interfaceInfo.uid) {
277 interfaceTypeInfos_.erase(pos);
278 }
279 }
280 }
281
GetInterfaceMetadata() const282 array_view<const InterfaceTypeInfo* const> Engine::GetInterfaceMetadata() const
283 {
284 return interfaceTypeInfos_;
285 }
286
GetInterfaceMetadata(const Uid & uid) const287 const InterfaceTypeInfo& Engine::GetInterfaceMetadata(const Uid& uid) const
288 {
289 static constexpr InterfaceTypeInfo invalidType {};
290
291 if (!interfaceTypeInfos_.empty()) {
292 const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), uid,
293 [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
294 if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == uid) {
295 return *(*pos);
296 }
297 }
298 return invalidType;
299 }
300
GetInstance(const Uid & uid) const301 IInterface* Engine::GetInstance(const Uid& uid) const
302 {
303 const auto& data = GetInterfaceMetadata(uid);
304 if (data.getInterface) {
305 return data.getInterface(const_cast<Engine&>(*this), data.token);
306 }
307 return nullptr;
308 }
309
CreateInstance(const Uid & uid)310 IInterface::Ptr Engine::CreateInstance(const Uid& uid)
311 {
312 const auto& data = GetInterfaceMetadata(uid);
313 if (data.createInterface) {
314 return IInterface::Ptr { data.createInterface(*this, data.token) };
315 }
316 return {};
317 }
318
GetInterface(const BASE_NS::Uid & uid) const319 const IInterface* Engine::GetInterface(const BASE_NS::Uid& uid) const
320 {
321 if (auto ret = IInterfaceHelper::GetInterface(uid)) {
322 return ret;
323 }
324 return resourceManager_.GetInterface(uid);
325 }
326
GetInterface(const BASE_NS::Uid & uid)327 IInterface* Engine::GetInterface(const BASE_NS::Uid& uid)
328 {
329 if (auto ret = IInterfaceHelper::GetInterface(uid)) {
330 return ret;
331 }
332 return resourceManager_.GetInterface(uid);
333 }
334
OnTypeInfoEvent(EventType type,array_view<const ITypeInfo * const> typeInfos)335 void Engine::OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos)
336 {
337 if (type == EventType::ADDED) {
338 for (const auto* info : typeInfos) {
339 if (info && info->typeUid == IEnginePlugin::UID && static_cast<const IEnginePlugin*>(info)->createPlugin) {
340 auto enginePlugin = static_cast<const IEnginePlugin*>(info);
341 if (std::none_of(plugins_.begin(), plugins_.end(),
342 [enginePlugin](const pair<PluginToken, const IEnginePlugin*>& pluginData) {
343 return pluginData.second == enginePlugin;
344 })) {
345 auto token = enginePlugin->createPlugin(*this);
346 plugins_.push_back({ token, enginePlugin });
347 }
348 }
349 }
350 } else if (type == EventType::REMOVED) {
351 for (const auto* info : typeInfos) {
352 if (!info || info->typeUid != IEnginePlugin::UID) {
353 continue;
354 }
355 auto enginePlugin = static_cast<const IEnginePlugin*>(info);
356 if (auto pos = std::find_if(plugins_.cbegin(), plugins_.cend(),
357 [enginePlugin](const pair<PluginToken, const IEnginePlugin*>& pluginData) {
358 return pluginData.second == enginePlugin;
359 });
360 pos != plugins_.cend()) {
361 if (enginePlugin->destroyPlugin) {
362 enginePlugin->destroyPlugin(pos->first);
363 }
364 plugins_.erase(pos);
365 }
366 }
367 }
368 }
369
GetVersion()370 string_view Engine::GetVersion()
371 {
372 return CORE_NS::GetVersion();
373 }
374
IsDebugBuild()375 bool Engine::IsDebugBuild()
376 {
377 return CORE_NS::IsDebugBuild();
378 }
379
CreateEngine(EngineCreateInfo const & createInfo)380 IEngine::Ptr CreateEngine(EngineCreateInfo const& createInfo)
381 {
382 auto engine = new Engine(createInfo);
383 return IEngine::Ptr { engine };
384 }
385
GetPlatform() const386 const IPlatform& Engine::GetPlatform() const
387 {
388 return *platform_;
389 }
390 CORE_END_NAMESPACE()
391