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 58CORE_BEGIN_NAMESPACE() 59namespace { 60#if (CORE_EMBEDDED_ASSETS_ENABLED == 1) 61// Core Rofs Data. 62extern "C" const uint64_t SIZEOFDATAFORCORE; 63extern "C" const void* const BINARYDATAFORCORE[]; 64#endif 65 66using BASE_NS::array_view; 67using BASE_NS::make_unique; 68using BASE_NS::pair; 69using BASE_NS::string; 70using BASE_NS::string_view; 71using BASE_NS::Uid; 72 73// This is defined in the CMake generated version.cpp 74void 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 90Engine::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 106Engine::~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 124CORE_NS::IEcs* IEcsInstance(IClassFactory&, const IThreadPool::Ptr&); 125 126IEcs::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 137IEcs::Ptr Engine::CreateEcs(IThreadPool& threadPool) 138{ 139 return IEcs::Ptr { IEcsInstance(*this, IThreadPool::Ptr { &threadPool }) }; 140} 141 142void 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 155void 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 176void Engine::UnloadPlugins() 177{ 178 for (auto& plugin : plugins_) { 179 if (plugin.second->destroyPlugin) { 180 plugin.second->destroyPlugin(plugin.first); 181 } 182 } 183} 184 185void 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 195bool Engine::TickFrame() 196{ 197 return TickFrame({ nullptr, size_t(0) }); 198} 199 200bool 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 228bool 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 242IImageLoaderManager& Engine::GetImageLoaderManager() 243{ 244 CORE_ASSERT_MSG(imageManager_, "Engine not initialized"); 245 return *imageManager_; 246} 247 248IFileManager& Engine::GetFileManager() 249{ 250 CORE_ASSERT_MSG(fileManager_, "Engine not initialized"); 251 return *fileManager_; 252} 253 254EngineTime Engine::GetEngineTime() const 255{ 256 return { previousFrameTime_ - firstTime_, deltaTime_ }; 257} 258 259const 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 271IInterface* 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 283void 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 291void 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 302array_view<const InterfaceTypeInfo* const> Engine::GetInterfaceMetadata() const 303{ 304 return interfaceTypeInfos_; 305} 306 307const 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 321IInterface* 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 330IInterface::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 339void 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 373string_view Engine::GetVersion() 374{ 375 return CORE_NS::GetVersion(); 376} 377 378bool Engine::IsDebugBuild() 379{ 380 return CORE_NS::IsDebugBuild(); 381} 382 383IEngine::Ptr CreateEngine(EngineCreateInfo const& createInfo) 384{ 385 auto engine = new Engine(createInfo); 386 return IEngine::Ptr { engine }; 387} 388 389const IPlatform& Engine::GetPlatform() const 390{ 391 return *platform_; 392} 393 394void Engine::Ref() 395{ 396 refCount_++; 397} 398 399void Engine::Unref() 400{ 401 if (--refCount_ == 0) { 402 delete this; 403 } 404} 405CORE_END_NAMESPACE() 406