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